【问题标题】:Decoupling via Interfaces in Go... Slice of interface implementors?通过 Go 中的接口解耦......接口实现者的片段?
【发布时间】:2015-01-30 23:49:11
【问题描述】:

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

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

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( ) 的函数签名(或者更糟糕的是,在接口定义中)?我并没有试图欺骗编译器或陷入疯狂的运行时技巧。我知道我可能会更改 Foo.Bars() 的实现以返回 []Foolike,但这似乎是愚蠢和错误的(为什么 A 必须知道 B 的任何事情?这打破了解耦的全部意义!)。

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

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

【问题讨论】:

  • 解耦是一回事。但是当你递归定义你的接口时,它只能做到这一点。然后包A 必须导入包B
  • 我建议将其发布到邮件列表 (groups.google.com/forum/#!forum/golang-nuts)。我认为开发人员经常去那里。然后你可以在这里发布他们的答案。
  • seong 的返回使 f.Bars 实际上返回 []Foolike 的答案是我看到的最好的问题描述——如果你能描述用例(比如,@987654329 @ 和 Bars 真的是,等等)也许有一个聪明的方法可以解决它,包括返回除了切片或其他东西之外的东西。但可能没有。
  • 感觉您使用的界面不正确。必须从界面中“导出”一些信息,例如“我们是界面的数组”,这似乎很奇怪。缺了点什么。你能告诉我们你想做什么吗?
  • 另请注意,包不一定与“解耦”相关。包通常代表一个非常一般意义上的“想法”。根据您的问题,您的包AB 应该在一个包中。循环导入或类似的依赖问题通常暗示你应该重新考虑你的包结构。

标签: interface go decoupling


【解决方案1】:

正如错误消息所述,您不能将[]FooLike[]*Foo 类型互换。

对于 []*Foo 切片,后备数组在内存中将如下所示:

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

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

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

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

因此,您的基础类型将需要返回接口类型的一部分才能使其工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-14
    • 1970-01-01
    • 2018-04-16
    • 1970-01-01
    • 1970-01-01
    • 2018-11-11
    • 2014-11-19
    • 1970-01-01
    相关资源
    最近更新 更多