通过Go中的接口解耦...接口实现器的片段?

时间:2014-12-02 16:35:52

标签: interface go decoupling

行。我知道这是一个常见问题解答,我认为答案是“放弃,它不会那样工作”,但我只是想确保我没有错过任何东西。

我仍然围绕着使用接口的最佳实践和规则。我有不同包中的代码,我宁愿保持解耦,这样的事情(不起作用,或者我不会在这里):

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视为不同的东西因为......( mumble 内存分配,严格输入 mumble )。

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

我想另一个选择是删除Bars()作为实现接口的要求,并依赖其他方法来强制执行要求。但是感觉不太理想(如果Bars()是唯一导出的方法呢?)。编辑:不,那不行,因为那时我不能在DoSomething()中使用Bars(),因为它没有在界面中定义。叹息。

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

1 个答案:

答案 0 :(得分:1)

正如错误消息所示,您无法互换地处理[]FooLike[]*Foo类型。

对于a []*Foo切片,支持数组在内存中看起来像这样:

| value1 | value2 | value3 | ... | valueN |

由于我们知道值将是*Foo类型,因此它们可以以直接的方式顺序存储。相反,[]FooLike切片中的每个元素可以是不同的类型(前提是它们符合FooLike)。因此支持数组看起来更像:

| type1 | value1 | type2 | value2 | type3 | value3 | ... | typeN | valueN |

因此,在类型之间进行简单的转换是不可能的:有必要创建一个新的切片并复制这些值。

因此,您的基础类型需要返回一个接口类型的片段才能生效。