Interface 通过Go中的接口进行解耦。。。接口实现者的切片?

Interface 通过Go中的接口进行解耦。。。接口实现者的切片?,interface,go,decoupling,Interface,Go,Decoupling,嗯。我知道这是一个常见问题,我想答案是“放弃,那样不行”,但我只是想确保我没有错过什么 我仍然在思考接口使用的最佳实践和规则。我有不同包中的代码,我更喜欢保持解耦,类似于这样(不起作用,否则我不会在这里): 因此,编译器会抱怨: cannot use f (type *A.Foo) as type Foolike in argument to B.DoSomething: *A.Foo does not implement Foolike (wrong type for Bars method

嗯。我知道这是一个常见问题,我想答案是“放弃,那样不行”,但我只是想确保我没有错过什么

我仍然在思考接口使用的最佳实践和规则。我有不同包中的代码,我更喜欢保持解耦,类似于这样(不起作用,否则我不会在这里):


因此,编译器会抱怨:

cannot use f (type *A.Foo) as type Foolike in argument to B.DoSomething:
*A.Foo does not implement Foolike (wrong type for Bars method)
    have Bars() ([]*A.Foo, error)
    want Bars() ([]Foolike, error)
现在,我发现[]傻瓜式本身并不是一个接口签名;这是一片愚蠢界面的特征。我想我也很奇怪编译器把[]*A.Foo和[]傻瓜式的东西视为不同的东西,因为。。。(mumble内存分配,严格键入mumble)

我的问题是:有没有一种正确的方法来实现我最终想要的,那就是让B.DoSomething()接受*a.Foo而不必导入a并在B.DoSomething()的函数签名中使用*a.Foo(或者更糟,在接口定义中)?我不想尝试欺骗编译器,也不想尝试疯狂的运行时技巧。我知道我可能会将Foo.bar()的实现更改为返回[]傻瓜式,但这似乎既愚蠢又错误(为什么A必须了解B?这打破了解耦的全部意义!)

我想另一种选择是将bar()作为实现接口的一项要求删除,并依赖其他方法来强制执行该要求。不过,这感觉不太理想(如果bar()是唯一导出的方法怎么办?)。编辑:不,那不行,因为我不能在DoSomething()中使用bar(),因为它没有在接口中定义。唉


如果我只是做错了™, 我会接受这一点,并找出一些其他的办法,但我希望我没有了解它应该如何工作。

正如错误消息所说,您不能互换地对待
[]傻瓜式
[]*Foo
类型

对于a
[]*Foo
片,备份阵列在内存中的外观如下:

| value1 | value2 | value3 | ... | valueN |
因为我们知道这些值的类型是
*Foo
,所以可以直接按顺序存储它们。相反,
[]傻瓜式
切片中的每个元素可以是不同的类型(只要它们符合
傻瓜式
)。因此,备份阵列看起来更像:

| type1 | value1 | type2 | value2 | type3 | value3 | ... | typeN | valueN |
因此,不可能在类型之间进行简单的强制转换:需要创建一个新的切片并复制这些值


因此,您的底层类型需要返回接口类型的一部分才能工作。

解耦是一回事。但是当您递归地定义接口时,它只能走这么远。然后包
A
必须导入包
B
。我建议将其发布到邮件列表()。我认为开发人员经常去那里。然后你可以在这里发布他们的答案。seong关于返回的答案是制造
f.Bars
实际上返回一个
[]傻瓜式的
是我看到的最好的问题描述——如果你能描述用例(比如
Foo
Bars
真正是什么,等等)也许有一个聪明的方法可以解决这个问题,那就是除了一片或者其他什么东西之外,还可以退回一些东西。但可能没有。感觉您使用的界面不对。从接口中“导出”一些信息似乎很奇怪,比如“我们是一个接口数组”。有些东西不见了。你能告诉我们你想做什么吗?还要注意,包不一定与“解耦”相关。包通常代表一个非常普遍意义上的“想法”。根据您的问题,您的软件包
A
B
应该在一个软件包中。循环导入或类似的依赖性问题通常暗示您应该重新考虑包结构。
| value1 | value2 | value3 | ... | valueN |
| type1 | value1 | type2 | value2 | type3 | value3 | ... | typeN | valueN |