2017-04-17 129 views
2

我目前有两个结构。Golang避免代码复制

type One struct { 
    Name string 
    Age int 
    ID int 
    Owner string 
} 

type Two struct { 
    ID int 
    Make string 
    Bags string 
    Age int 
    Owner string 
} 

这些结构映射到表在DB中,我使用的接口,以提供接入到数据库和内容。在这种情况下,只需根据所有者列出One和Two中的数据。

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) 
} 

我是相当新的去,所以我想知道什么是减少这里看到的代码重复的惯用方法是什么?如果我要添加更多的结构,那么它会很难处理,因为需要大量的代码重复。

感谢您的帮助!

+0

在我发布我的答案之前,我有一个问题。是'db.client.GetAll'和'inteface {}'的第二个参数吗? – RayfenWindspear

+0

我投票结束这个问题作为题外话,因为这个问题属于https://codereview.stackexchange。com/ –

+2

@PaulHankin他没有要求进行一般的代码审查,他问的是如何使用Go语言的具体问题。对于我来说这似乎合法。 – Adrian

回答

1

假设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 
} 

结果转换为一个地图是有点困难,因为围棋缺乏仿制药,而你的结构没有可用于连接它们的方法。这是可能的,但至少需要在每种类型上创建一个方法,创建一个接口,然后返回map[int]hasID,调用者然后必须将值返回到结构类型以访问任何其他字段。不是最佳的,但可行的。但是,上面的解决方案至少会让你消除重复代码的很大一部分。

1

只是添加到接受的答案,如果您使用google.golang.org/appengine/datastore您不需要循环的键,除非你想。

GetAll docs:(重点煤矿)

GETALL运行在给定的情况下查询并返回 匹配查询,以及追加值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) 
    } 
} 

顺便说一句,如果你想控制你的类型,如OneTwo等是如何从数据存储中加载的,你可以让它们实现PropertyLoadSaver接口。

+0

这非常有帮助,谢谢。 – Harry