通过 Go 中的接口解耦......接口实现者的一部分?

好的。我知道这是一个常见问题解答,我认为答案是“放弃,那样不行”,但我只是想确保我没有遗漏任何东西。


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


package A


type Foo struct {}


func (f *Foo) Bars() ([]*Foo, error) {

    foos := make([]*Foo, 0)


    // some loop which appends a bunch of related *Foo to foos

    return foos, nil

}

package B


type Foolike interface {

    Bars() []Foolike

}


func DoSomething(f Foolike) error {

    // blah

}

有了这个,编译器抱怨:


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)

现在,我认为 []Foolike本身不是接口签名;它是 Foolike 接口切片的签名。我想我也神交编译器对待[] * A.Foo和[] Foolike不同的东西,因为...(喃喃内存分配,严格类型喃喃)。


我的问题是:有没有一种正确的方法来做我最终想要的,那就是让 B.DoSomething() 接受一个 *A.Foo 而不必导入 A 并在 B.DoSomething() 中使用 *A.Foo函数签名(或更糟,在接口定义中)?我并没有试图欺骗编译器或陷入疯狂的运行时技巧。我知道我可能会更改 Foo.Bars() 的实现以返回 []Foolike,但这似乎很愚蠢和错误(为什么 A 必须了解 B 的任何信息?这打破了解耦的重点!)。

不,那将不起作用,因为那样我就不能在 DoSomething() 中使用 Bars(),因为它没有在接口中定义。叹。

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


弑天下
浏览 259回答 1
1回答

大话西游666

正如错误消息所说,您不能将[]FooLike和[]*Foo类型互换。对于[]*Foo切片,后备数组在内存中看起来像这样:| value1 | value2 | value3 | ... | valueN |由于我们知道这些值将是 type *Foo,因此可以以直接的方式顺序存储它们。相反,[]FooLike切片中的每个元素可以是不同的类型(前提是它们符合FooLike)。所以后备数组看起来更像是:| type1 | value1 | type2 | value2 | type3 | value3 | ... | typeN | valueN |所以不可能在类型之间进行简单的转换:有必要创建一个新切片并复制值。因此,您的基础类型需要返回接口类型的一部分才能使其正常工作。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go