Advertisement

100 Go Mistakes and How to Avoid Them 之代码结构和项目组织(三)

阅读量:
六 生产端的接口

然而,在前面的例子中, 我们洞察了接口的作用体现; 尽管如此, 有一个误解经常让开发者困惑: 那就是interface到底放在哪一端.

在讨论这个话题之前,请先明确本节中所涉及的相关术语的具体含义:

生产端:接口的定义与具体实现在同一个包里面
在这里插入图片描述
客户端(应用端):接口定义在外部包,在外部进行使用
在这里插入图片描述

普遍采用的做法是在运行时环境中先创建接口,并同时完成具体的逻辑实现。那些具备C#或Java开发经验的开发者往往对此类场景有偏好。对于Go语言而言,在大多数情况下这种做法是不被推荐的。

为了更好地理解这一问题,在接下来的例子中将进行详细讨论。为此,我们设计了一个专用的数据包,并实现了对客户数据的存储与检索功能;在此专用数据包中,并为了确保所有调用的一致性与规范性要求,在设计时我们特别规定了必须遵循的标准接口。
其中心理念是实现统一性和规范性要求的同时提升系统的可扩展性和维护性。

复制代码
    package store
    
    type CustomerStorage interface {
        StoreCustomer(customer Customer) error
        GetCustomer(id string) (Customer, error)
        UpdateCustomer(customer Customer) error
        GetAllCustomers() ([]Customer, error)
        GetCustomersWithoutContract() ([]Customer, error)
        GetCustomersWithNegativeBalance() ([]Customer, error)
    }
    
    
      
      
      
      
      
      
      
      
      
      
    
    AI助手

我们倾向于认为,在生产端构建和暴露接口上拥有更多的合理理由。或许这种方法具有显著优势——它即强调了客户端与具体实现之间的分离。也许我们可以预见这种做法会导致客户端在测试时重复创建测试用例?对于Go语言而言,这并非最佳实践。

就如我们之前讨论过的那样,在Go语言中,默认情况下接口是一种隐式的实现方式。与那些显式实现了接口的语言相比,在大多数情况下,默认情况下接口的作用类似于改变了游戏规则的一种机制:真正的摘要不应是由开发者主动定义和规划出来的(我的理解是,在开发过程中我们会根据实际情况选择何时进行摘要)。这表明开发团队无需将特定类型的摘要强制提供给所有客户端方;相反地,则应由客户端自行决定是否引入这些摘要,并确定最适合于自身需求的最佳层次。

在前面的例子中,可能存在一些不太感兴趣的用户对于代码解耦操作。然而,其他一些开发者可能会希望进行代码解耦,但他们可能只关注某个特定的方法,如GetAllCustomers,而无需对其它部分进行改动。在这种情况下,他们可以选择针对该特定方法创建一个接口,并在外部组件中引用customer数据体以实现相同的功能

复制代码
    package client
    
    type customersGetter interface {
        GetAllCustomers() ([]store.Customer, error)
    }
    
    
      
      
      
      
      
    
    AI助手
按照上面的包组织,它的结果如下:
在这里插入图片描述
注意以下几点:

customersGetter接口的代码仅位于client包内,并未对外提供(外部不可访问)。

2 观察图形可以看出存在循环依赖现象。然而,在代码结构中,并没有从 storeclient 的直接数据传递路径。这是因为由于接口设计是隐式的,并未在实现层面上显式地定义数据交换机制的原因。因此这种方法无法存在于那些必须显式实现接口语言中。

核心观点在于,《client》包最大限度地制定所需抽象(例如仅包含一个方法),这遵循接口分离原则(即SOLID原则中的I),其定义为:无需强迫实现未被使用的功能。

由此可见,在此例中,最佳实践方案要求我们在生产层明确具体实现细节,并将该方案传递给客户端系统。这种安排不仅能够使客户端具备高度的灵活性与自主权(即允许客户端自主决定其使用方式),并且还能够为后续的功能扩展预留空间(即根据客户端的具体需求判断是否采用该抽象结构)。

为了全面阐述这一方法,在生产端定义接口的同时,在标准库中也会使用。例如,在encoding包中就实现了接口。这些接口则会在其他子包中具体实现。那么在这种情况下是否错误地应用了这一方法?当然不是;然而在这种情况下的应用,并非错误地应用了这一方法。但其应用范围却覆盖了整个标准库。该语言(Go)的设计者深知创建这样的一个抽象能够带来显著的价值。因此在不确定该抽象是否能为你未来的工作带来便利或者能证明其实用性之前,请避免创建这样的抽象。

通常情况下,默认的接口设计建议倾向于将接口(创建)功能分配在客户端。然而,在某些特定场景中,
如果能够确认这种摘要对客户端有益,则可以将其部署在服务端(比如所有客户端都可能需要这种功能时),而无需担心是否会干扰其他地方的工作。
同时,
这表明最佳实践是将其部署在生产端。
此外,
如果我们确定要这么做,则需确保该摘要模块尽可能小,
以保证其可用性和组合能力。

全部评论 (0)

还没有任何评论哟~