接口及其实现的Golang包/目录结构?

时间:2017-09-11 15:48:45

标签: go interface

让我们考虑一下您的典型网络应用程序。在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应用程序模板,但这些模板中没有一个(超出那些令人难以置信的准分子模板)都能为这种结构提供一个优雅的解决方案。大多数(如果不是全部)它们只是完全省略接口来解决它,这是不可接受的。

任何提示或提示?

2 个答案:

答案 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()已注册的单独抽象的东西?