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

aet*_*eus 2 interface go

让我们考虑您的典型Web应用程序。在MVC应用程序中,您最终可能希望引入一个“服务”层,该层抽象出诸如用户注册之类的复杂业务逻辑。因此,在您的控制器中,您将传递一个services.User结构实例,并只需对其调用Register()方法。

现在,如果services.User仅仅是一个结构,我们可以拥有一个相对简单的源代码结构,如下所示:

- [other directories here]/
- services/
    - user.go
    - [other service structs here]
- main.go
Run Code Online (Sandbox Code Playgroud)

并且services/user.go看起来像这样:

package services

type User struct { ... }
func NewUserService(){ ... }
func (u User) Register() { ... }
Run Code Online (Sandbox Code Playgroud)

到目前为止,所有这些都相当容易阅读。假设我们将其进一步发展。本着使我们的Web应用程序易于测试的精神,我们将所有Service结构转换为Service接口。这样,我们可以轻松模拟它们以进行单元测试。为此,我们将创建一个“ AppUser”结构(用于实际应用程序)和一个“ MapUser”结构(用于模拟目的)。将接口和实现放在同一个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]
Run Code Online (Sandbox Code Playgroud)

如您所知,这使services程序包和目录变得更加难以处理-您可以轻松想象一下,通过十二种不同的接口看起来会多么混乱,每个接口至少具有至少一种实现。如果我更改了中的User接口user.go,则必须在目录列表中遍历所有内容,以查找要更改的所有实现,这一点都不理想。

另外,当您键入内容services.New(...)并收到50条左右的自动完成建议时,它变得非常疯狂。该services包已成为只是一个蹒跚的庞然大物。

我必须解决的最简单的想法之一就是违背惯例并接受重复:

- services/
    - userService/
        - app.go // the AppUser struct
        - map.go // the MapUser struct
        - interface.go  // the User interface
    - [other services here]
Run Code Online (Sandbox Code Playgroud)

这会将所有与UserService相关的代码保存在一个逻辑独立的程序包中。但是必须不断提及userService.UserService是非常丑陋的。

我已经看过各种Web应用程序模板,但它们(除了那些简直是准系统之外的模板)都没有针对此结构的优雅解决方案。他们中的大多数(如果不是全部)只是完全省略接口来解决它,这是不可接受的。

Fli*_*mzy 5

您的界面实现应(通常)位于单独的程序包中。这不是一成不变的规则,您可能经常在接口定义旁边有一个默认实例。

但是,请考虑一个更抽象的示例:键/值存储接口。它可能由文件系统,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
Run Code Online (Sandbox Code Playgroud)

在您的特定示例中,至少我会将实现存储在接口定义下的一级:

- services/
    - userService/
        - interface.go  // the User interface
        - app
            app.go // the AppUser struct
        - map
            map.go // the MapUser struct
    - [other services here]
Run Code Online (Sandbox Code Playgroud)

这样,在map.New()和之间就不会造成混乱app.New(),并且您的内部数据结构不会互相影响等等。