让我们考虑一下您的典型网络应用程序。在MVC应用程序中,您最终可能希望引入一个" Service"抽象复杂业务逻辑的层,例如用户注册。因此,在您的控制器中,您传递services.User
结构的实例,只需在其上调用Register()
方法即可。
现在,如果services.User
只是一个结构,我们可以有一个相对简单的源代码结构,如下所示:
- [other directories here]/
- services/
- user.go
- [other service structs here]
- main.go
services/user.go
看起来像这样:
package services
type User struct { ... }
func NewUserService(){ ... }
func (u User) Register() { ... }
到目前为止,这一切都相当容易阅读。让我们说我们更进一步。本着使我们的Web应用程序易于测试的精神,我们将所有服务结构转换为服务接口。这样,我们可以轻松地模拟它们进行单元测试。为此,我们将创建一个" AppUser" struct(用于实际应用程序)和" MapUser" struct(用于嘲弄)。将接口和实现放在同一个services
目录中是有道理的 - 毕竟它们仍然是service
代码。
我们的services
文件夹现在看起来像这样:
- services/
- app_user.go // the AppUser struct
- [other services here]
- map_user.go // the MapUser struct
- [other services here]
- user.go // the User interface
- [other service structs here]
正如您所知,这使得services
包和目录更难以处理 - 您可以很容易地想象它与十几个不同的界面看起来有多混乱,每个界面至少有一个至少1个实现。如果我更改User
中的user.go
界面,我必须在整个目录列表中进行调查,以找到要更改的所有实现,这根本不是理想的。< / p>
此外,当您输入services.New(...)
时,它会变得相当疯狂,并且您可能会收到大约50个左右的自动填充建议。 services
包已经变成了一个怪物。
我必须解决的一个最简单的想法是反对惯例并接受重复:
- services/
- userService/
- app.go // the AppUser struct
- map.go // the MapUser struct
- interface.go // the User interface
- [other services here]
这将所有与UserService相关的代码保存在一个逻辑的自包含的包中。但是不得不经常提到userService.UserService
是非常难看的。
我已经查看了各种各样的Web应用程序模板,但这些模板中没有一个(超出那些令人难以置信的准分子模板)都能为这种结构提供一个优雅的解决方案。大多数(如果不是全部)它们只是完全省略接口来解决它,这是不可接受的。
任何提示或提示?
答案 0 :(得分:4)
您的界面实现应该(通常)存在于单独的包中。这不是一个硬性规则,您可能经常在接口定义旁边有一个默认实例。
但是想一个更抽象的例子:一个Key / Value商店界面。它可能由文件系统,SQL数据库,Amazon S3或内存数据结构支持。
您通常会在一个地方定义您的界面,例如myproject/kvstore/kvstore.go
。
然后,您将在其他地方定义您的实施。甚至可能在完全不同的存储库中。
- myproject
- kvstore
kvstore.go
memory.go -- A default implementation, non-persistent
- filesystem
filesystem.go -- A file-persistent implementation
- yourproject
- sqlite -- An implementation backed by sqlite
在您的具体示例中,至少我会将我的实现存储在接口定义下面一级:
- services/
- userService/
- interface.go // the User interface
- app
app.go // the AppUser struct
- map
map.go // the MapUser struct
- [other services here]
然后map.New()
和app.New()
之间没有混淆,您的内部数据结构也不会互相踩踏等。
答案 1 :(得分:1)
另一种可能的方法是重新安排您的包裹。而不是services
具有更多赋权的对象集合,反映在目录结构中:
- users/
- app.go
- map.go
- interface.go
哪个可以支持users.New()
方法,该方法将被注册为委派默认值。这可以允许您将基于存根/内存的接口保留在与生产实现不同的文件中。
如果您需要服务注册表或某些可能是users.New()
已注册的单独抽象的东西?