为了账号安全,请及时绑定邮箱和手机立即绑定

在 Go 中注册没有循环依赖的包

在 Go 中注册没有循环依赖的包

Go
万千封印 2021-09-27 10:54:40
我有一个中央包,它提供了其他包所依赖的几个接口(让我们称之为一个Client)。那些其他包提供了这些第一个接口 ( UDPClient, TCPClient) 的几种实现。我Client通过调用NewClient中央包来实例化 a ,它从一个依赖包中选择并调用适当的客户端实现。当我想告诉中央包有关其他包的信息时,这会崩溃,因此它知道它可以创建哪些客户端。那些依赖的客户端实现也会导入中央包,从而创建 Go 不允许的循环依赖。最好的前进方向是什么?我不想将所有这些实现混合在一个包中,创建一个单独的注册表包似乎有点过分。目前,我将每个实现都注册到中央包中,但这要求用户知道将每个实现导入到每个使用客户端的单独二进制文件中。import (    _ udpclient    _ tcpclient    client)
查看完整描述

1 回答

?
慕哥6287543

TA贡献1831条经验 获得超10个赞

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


1) 没有“中央”注册表

这方面的例子是不同的哈希算法。该crypto包仅定义了Hash接口(类型及其方法)。具体的实现在不同的包中(实际上是子文件夹,但不需要),例如crypto/md5和crypto/sha256。


当你需要一个“散列器”时,你明确说明你想要哪个并实例化那个,例如


h1 := md5.New()

h2 := sha256.New()

这是最简单的解决方案,它还为您提供了良好的分离:hash包不必知道或担心实现。


如果您知道或者您可以决定您想要的实现方式,那么这是首选的解决方案。


2) 使用“中央”注册表

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


这方面的一个例子是image包。该包定义了Image接口及其几个实现。在不同的包中定义了不同的图像格式,例如image/gif、image/jpeg和image/png。


该image包有一个Decode()函数,它Image从指定的io.Reader. 通常不知道来自阅读器的图像类型是什么,因此您不能使用特定图像格式的解码器算法。


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


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


那么最好的方法是什么?


取决于你的要求是什么。如果您知道或可以决定您需要哪种实现,请选择 #1。如果您无法决定或不知道并且需要可扩展性,请选择#2。


...或者使用下面介绍的#3。


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

您仍然可以享受“中央”注册表的便利,将接口和实现分开,但要付出“自动扩展性”的代价。


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


并且您创建了一个包含pf您想要的“工厂”方法的包,例如pf.NewClient(). 该pf包可以指包pa,pb,pi而不创建循环依赖关系。


查看完整回答
反对 回复 2021-09-27
  • 1 回答
  • 0 关注
  • 158 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信