Golang代码结构化

时间:2017-02-18 16:28:57

标签: go

是否值得在结构中对方法进行分组: 例如:

type UserManager struct {
    DB *sql.DB
}

func (m UserManager) Insert (u User) error {...}
func (m UserManager) Delete (u User) error {...}
...

或仅仅支持单独的功能更简单。

func InsertUser (u User, db *sql.DB) error {...}

虽然第一种方法起初看起来更简单,但在将来这种方式中,包中可能存在许多功能。我应该为每个域聚合制作单独的包吗?在我看到目前为止的例子中,只有model个包。 我一直主要使用OO语言,所以需要一些建议来获取最佳实践。

2 个答案:

答案 0 :(得分:1)

你的第二个建议是不好的去代码!为什么?因为在最好的情况下,函数应该将接口作为输入。

所以InsertUser函数看起来应该是这样的,它会将你的第一个与你的第二个建议结合起来:

type Inserter interface {
   Insert(User)error
}
func InsertUser(i Inserter) error {...}

在这种情况下,您的功能测试很简单,因为您可以轻松模拟插入器。

答案 1 :(得分:1)

要么是,要么两者都没有 - 在我看来这并不重要,因为惯用的方法是使用接口来组织这些概念:

package user

type User ...

type Inserter interface { Insert(User) error }
type Deleter interface { Delete(User) error }
type Manager interface { Inserter, Deleter } // bloated interface
在这种情况下,

User可能是一个具体的行类型,就像你的例子中一样,但是人们可以将它变成一个不提及这些类型的接口。

如果编写引用这些接口的函数,则可以使用embedding & promoted fields快速粘合在一起。

在您的情况下,显而易见的是坚持第一种实现方式要简单得多:

type userManager struct { ... }
func (userManager) Insert(u User) error { ... }
func (userManager) Delete(u User) error { ... }

userManager是一种私有类型,因此只要它能够满足公共接口,就可以无需担心地进行更改。

保持接口与实现分离使得更容易使它们变窄,因此不仅仅是拥有一个"用户管理器"或者其他什么,你可以找到你真正需要的任务接口。顺便说一句,这种方法具有很好的属性,它很适合object capability model,这有助于简化基于角色的访问控制等事情。