Golang 避免代码重复
Golang avoiding code duplication
我现在有两个结构。
type One struct {
Name string
Age int
ID int
Owner string
}
type Two struct {
ID int
Make string
Bags string
Age int
Owner string
}
这些结构映射到数据库中的 table,我使用一个接口来提供对数据库和内容的访问。在这种情况下,只是基于所有者的一和二中的数据列表。
type dbInterface interface {
ListOnesByOwner(owner string) ([]*One, error)
LitsTwosByOwner(owner string) ([]*Two, error)
}
除了结构之外,列表函数是相同的。
func (db *DB) ListOnes(owner string) ([]*One, error) {
ones = make([]*One, 0)
q := db.NewQuery("One").
Filter("Owner =", owner).
Order("Name")
keys, err := db.client.GetAll(q, &ones)
for i, k := range keys {
ones[i].ID = k.ID
}
return ones, nil
}
func (db *DB) ListTwos(owner string) ([]*Two, error) {
twos = make([]*Two, 0)
q := db.NewQuery("Two").
Filter("Owner =", owner).
Order("Name")
keys, err := db.client.GetAll(q, &twos)
for i, k := range keys {
twos[i].ID = k.ID
}
return twos, nil
}
func main() {
ones, err := DB.ListOnesByOwner(user.ID)
twos, err := DB.ListTwosByOwner(user.ID)
}
我是 GO 的新手,所以我想知道减少这里看到的代码重复的惯用方法是什么?如果我要添加更多的结构,那么它会很笨重,因为需要大量的代码重复。
感谢您的帮助!
假设 db.client.GetAll
将 interface{}
作为它的第二个参数,看起来是这样,你实际上可以把它干掉:
func (db *DB) dryGet(owner, table string, result interface{}) error {
q := db.NewQuery(table).Filter("Owner =", owner).Order("Name")
keys,err := db.client.GetAll(q, &result)
return err
}
将结果转换为映射有点困难,因为 Go 缺少泛型,并且您的结构没有可用于连接它们的方法。这是可能的,但至少需要在每种类型上创建一个 getID
方法,创建一个 hasID
接口,然后返回一个 map[int]hasID
,然后调用者必须将其转换为返回结构类型的值以访问任何其他字段。不是最优的,但可行。但是,上述解决方案至少可以让您消除很大一部分重复代码。
只是添加到已接受的答案中,如果您使用的是 google.golang.org/appengine/datastore,则不需要循环键,除非您愿意。
来自 GetAll docs:(强调我的)
GetAll runs the query in the given context and returns all keys that
match that query, as well as appending the values to dst.
因此,您可以将两种方法简化为如下所示:
func (db *DB) ListOnes(owner string) ([]*One, error) {
ones = make([]*One, 0)
q := db.NewQuery("One").
Filter("Owner =", owner).
Order("Name")
if _, err := db.client.GetAll(q, &ones); err != nil {
return nil, err
}
return ones, nil
}
func (db *DB) ListTwos(owner string) ([]*Two, error) {
twos = make([]*Two, 0)
q := db.NewQuery("Two").
Filter("Owner =", owner).
Order("Name")
if _, err := db.client.GetAll(q, &twos); err != nil {
return nil, err
}
return twos, nil
}
这仍然是很多重复,因此您现在可以使用类似于已接受答案中的方法来概括您的代码,例如:
type dbInterface interface {
ListByType(owner, typ string, dst interface{}) ([]*datastore.Key, error)
}
func (db *DB) ListByType(owner, typ string, dst interface{}) ([]*datastore.Key, error) {
q := db.NewQuery(typ).
Filter("Owner =", owner).
Order("Name")
return db.client.GetAll(q, dst)
}
您可以像这样使用该实现:
func main() {
// ignore keys if you don't need them
ones := []*One{}
if _, err := DB.ListByType(user.ID, "One", &ones); err != nil {
panic(err)
}
// use keys if you need them
twos := []*Two{}
keys, err := DB.ListByType(user.ID, "Two", &twos)
if err != nil {
panic(err)
}
}
顺便说一下,如果您想控制类型 One
和 Two
等从数据存储区加载的方式,您可以让它们实现 PropertyLoadSaver界面。
我现在有两个结构。
type One struct {
Name string
Age int
ID int
Owner string
}
type Two struct {
ID int
Make string
Bags string
Age int
Owner string
}
这些结构映射到数据库中的 table,我使用一个接口来提供对数据库和内容的访问。在这种情况下,只是基于所有者的一和二中的数据列表。
type dbInterface interface {
ListOnesByOwner(owner string) ([]*One, error)
LitsTwosByOwner(owner string) ([]*Two, error)
}
除了结构之外,列表函数是相同的。
func (db *DB) ListOnes(owner string) ([]*One, error) {
ones = make([]*One, 0)
q := db.NewQuery("One").
Filter("Owner =", owner).
Order("Name")
keys, err := db.client.GetAll(q, &ones)
for i, k := range keys {
ones[i].ID = k.ID
}
return ones, nil
}
func (db *DB) ListTwos(owner string) ([]*Two, error) {
twos = make([]*Two, 0)
q := db.NewQuery("Two").
Filter("Owner =", owner).
Order("Name")
keys, err := db.client.GetAll(q, &twos)
for i, k := range keys {
twos[i].ID = k.ID
}
return twos, nil
}
func main() {
ones, err := DB.ListOnesByOwner(user.ID)
twos, err := DB.ListTwosByOwner(user.ID)
}
我是 GO 的新手,所以我想知道减少这里看到的代码重复的惯用方法是什么?如果我要添加更多的结构,那么它会很笨重,因为需要大量的代码重复。
感谢您的帮助!
假设 db.client.GetAll
将 interface{}
作为它的第二个参数,看起来是这样,你实际上可以把它干掉:
func (db *DB) dryGet(owner, table string, result interface{}) error {
q := db.NewQuery(table).Filter("Owner =", owner).Order("Name")
keys,err := db.client.GetAll(q, &result)
return err
}
将结果转换为映射有点困难,因为 Go 缺少泛型,并且您的结构没有可用于连接它们的方法。这是可能的,但至少需要在每种类型上创建一个 getID
方法,创建一个 hasID
接口,然后返回一个 map[int]hasID
,然后调用者必须将其转换为返回结构类型的值以访问任何其他字段。不是最优的,但可行。但是,上述解决方案至少可以让您消除很大一部分重复代码。
只是添加到已接受的答案中,如果您使用的是 google.golang.org/appengine/datastore,则不需要循环键,除非您愿意。
来自 GetAll docs:(强调我的)
GetAll runs the query in the given context and returns all keys that match that query, as well as appending the values to dst.
因此,您可以将两种方法简化为如下所示:
func (db *DB) ListOnes(owner string) ([]*One, error) {
ones = make([]*One, 0)
q := db.NewQuery("One").
Filter("Owner =", owner).
Order("Name")
if _, err := db.client.GetAll(q, &ones); err != nil {
return nil, err
}
return ones, nil
}
func (db *DB) ListTwos(owner string) ([]*Two, error) {
twos = make([]*Two, 0)
q := db.NewQuery("Two").
Filter("Owner =", owner).
Order("Name")
if _, err := db.client.GetAll(q, &twos); err != nil {
return nil, err
}
return twos, nil
}
这仍然是很多重复,因此您现在可以使用类似于已接受答案中的方法来概括您的代码,例如:
type dbInterface interface {
ListByType(owner, typ string, dst interface{}) ([]*datastore.Key, error)
}
func (db *DB) ListByType(owner, typ string, dst interface{}) ([]*datastore.Key, error) {
q := db.NewQuery(typ).
Filter("Owner =", owner).
Order("Name")
return db.client.GetAll(q, dst)
}
您可以像这样使用该实现:
func main() {
// ignore keys if you don't need them
ones := []*One{}
if _, err := DB.ListByType(user.ID, "One", &ones); err != nil {
panic(err)
}
// use keys if you need them
twos := []*Two{}
keys, err := DB.ListByType(user.ID, "Two", &twos)
if err != nil {
panic(err)
}
}
顺便说一下,如果您想控制类型 One
和 Two
等从数据存储区加载的方式,您可以让它们实现 PropertyLoadSaver界面。