Golang代码结构化

huangapple go评论93阅读模式
英文:

Golang code structuring

问题

将方法分组到结构体中是否值得:
例如:

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包。
我主要使用面向对象的语言工作,所以在这里需要一些建议,了解Go的最佳实践。

英文:

Is it worth to group methods in structs:
For example:

type UserManager struct {
    DB *sql.DB
}

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

Or is it simpler support just separate functions.

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

While second approach looks simpler at first, in future this way, there may be to many functions in package. Should I make separate package for every domain aggregate? In examples, I've seen so far, there is just model package.
I've been working mainly with OO languages so need some advice for go best practices here.

答案1

得分: 1

你的第二个建议对于编码来说不太好!为什么呢?因为在最好的情况下,一个函数应该以接口作为输入。

所以一个InsertUser函数应该像这样,并且它会结合你的第一个和第二个建议:

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

在这种情况下,测试你的函数很容易,因为你可以轻松地模拟插入器。

英文:

Your second suggestion is not good go code! Why? Because in the best case a function should take interfaces as an input.

So a InsertUserfunction should look something like that and it would combine your first with your second suggestion:

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

In that case testing of your function is easy, because you can easy mock the inserter.

答案2

得分: 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 可能是一个具体的行类型,就像你的示例中一样,但也可以将其作为一个不提及这些类型的接口。

如果你编写引用这些接口的函数,那么你可以快速地使用嵌入和提升字段进行组合。

在你的情况下,坚持第一种实现风格显然更简单:

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

userManager 是一个私有类型,因此可以随意更改,只要它继续满足公共接口的要求。

将接口与实现解耦使得将它们变窄变得更容易,因此不仅仅是拥有一个“用户管理器”之类的东西,你还可以找出你真正需要的任务接口。顺便说一句,这种方法与对象能力模型非常契合,有助于简化诸如基于角色的访问控制之类的事情。

英文:

Either, or neither - it really doesn't matter in my opinion because the idiomatic approach would be to organize these concepts using interfaces:

package user

type User ...

type Inserter interface { Insert(User) error }
type Deleter interface { Delete(User) error }
type Manager interface { Inserter, Deleter } // bloated interface

User in this case is probably a concrete row type like in your example, but one could make the case for making it too into an interface that doesn't mention those types.

If you write functions that reference these interfaces, then you can quickly glue together using embedding & promoted fields.

In your case it's obvious that sticking to the first implementation style is much simpler:

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

userManager is a private type, so it can be changed without concern, as long as it keeps satisfying the public interfaces.

Keeping the interfaces decoupled from the implementation makes it much easier to make them narrow, so instead of just having a "user manager" or something, you can find out which interfaces you really need for the tasks. Incidentally, this approach has the nice property that it fits well with the object capability model, which helps to simplify things like role based access control.

huangapple
  • 本文由 发表于 2017年2月19日 00:28:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/42317700.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定