小编典典

在Go中注册程序包而无需循环依赖

go

我有一个中央程序包,它提供了其他程序包所依赖的几个接口(我们称其为Client)。这些其他的包,提供的第一类接口的几种实现(UDPClientTCPClient)。我Client通过调用NewClient中央程序包实例化一个,它从一个相关程序包中选择并调用适当的客户端实现。

当我想将其他软件包告诉中央软件包时,这变得很困难,因此它知道可以创建哪些客户端。这些从属客户端实现还导入中央程序包,从而创建Go不允许的循环依赖项。

最好的前进方向是什么?我不希望将所有这些实现混在一起放在一个程序包中,而创建一个单独的注册表程序包似乎有些过头了。目前,每个实现都在中央程序包中进行注册,但这需要用户知道如何在每个使用客户端的二进制文件中导入每个实现。

import (
    _ udpclient
    _ tcpclient
    client
)

阅读 316

收藏
2020-07-02

共1个答案

小编典典

标准库通过多种方式解决了此问题:

1)没有“中央”注册表

这样的示例是不同的哈希算法。该crypto包仅定义Hash接口(类型及其方法)。具体实现在不同的封装(其实子文件夹,但并不需要如此),例如crypto/md5crypto/sha256

当您需要“哈希器”时,您可以明确说明您要的实例并实例化该实例,例如

h1 := md5.New()
h2 := sha256.New()

这是最简单的解决方案,并且还可以为您提供良好的分离:hash程序包不必了解或担心实现。

如果您知道或可以先决定要使用哪种实现,则这是首选的解决方案。

2)具有“中央”注册表

这基本上是您建议的解决方案。实现必须以某种方式注册自己(通常在包init()函数中)。

image包装就是一个例子。该软件包定义了Image接口及其一些实现。在image/gifimage/jpeg和等不同的程序包中定义了不同的图像格式image/png

所述image封装具有Decode()其解码并返回一个函数Image从指定io.Reader。通常不知道哪种类型的图像来自阅读器,因此您不能使用特定图像格式的解码器算法。

在这种情况下,如果我们希望图像解码机制是可扩展的,那么注册是不可避免的。最干净的方法是在包init()函数中,该函数通过在导入时为包名指定空白标识符来触发。

请注意,此解决方案还使您可以使用特定的实现对图像进行解码Decode(),例如,具体的实现也提供了功能png.Decode()


那么最好的方法呢?

取决于您的要求。如果您知道或可以决定所需的实现,请选择#1。如果您不确定还是不知道并且需要可扩展性,请选择#2。

…或者继续下面列出的#3。

3)提出第三个解决方案:“自定义”注册表

您仍然可以方便地将“中央”注册表与界面和实现分开,而以“自动扩展性”为代价。

这个想法是你在package中有接口pi。你必须在包的实现papb等等。

然后,您将创建一个pf具有所需“工厂”方法的程序包,例如pf.NewClient()。该pf包可以指包papbpi而不创建循环依赖关系。

2020-07-02