假设我必须在两个不同的包中实现两个不同的接口(在两个不同的分离项目中)。
我在包A
package A
type interface Doer {
Do() string
}
func FuncA(Doer doer) {
// Do some logic here using doer.Do() result
// The Doer interface that doer should implement,
// is the A.Doer
}
并在包B
package B
type interface Doer {
Do() string
}
function FuncB(Doer doer) {
// some logic using doer.Do() result
// The Doer interface that doer should implement,
// is the B.Doer
}
在我的main
包
package main
import (
"path/to/A"
"path/to/B"
)
type C int
// this method implement both A.Doer and B.Doer but
// the implementation of Do here is the one required by A !
func (c C) Do() string {
return "C now Imppement both A and B"
}
func main() {
c := C(0)
A.FuncA(c)
B.FuncB(c) // the logic implemented by C.Do method will causes a bug here !
}
如何应对这种情况?
答案 0 :(得分:7)
使用其他语言的经验告诉我们,使用同名但不同签名的各种方法偶尔会有用,但在实践中也可能会令人困惑和脆弱。
仅按名称进行匹配并要求在类型中保持一致性是Go系统类型系统中的主要简化决策。
在您的情况下,您将满足两个接口。
您可以通过执行以下操作来测试对象(接口类型)是否满足另一个接口类型A.Doer
:
if _, ok := obj.(A.Doer); ok {
}
OP补充道:
但
Do
方法中实现的满足A
的逻辑与B
中的逻辑完全不同。
然后你需要在你的对象周围实现一个包装:
DoerA
,其中您的对象C
作为字段,并以满足A.Do()
应该如何工作的方式实施A.Do()
DoerB
,它与您的字段具有相同的对象C
,并以满足B.Do()
应该如何工作的方式实现B.Do()
这样,您就会知道要将哪个Doer传递给期望A.Doer
或B.Doer
的函数。
您不必在原始对象Do()
上实施C
方法,这将无法应对A.Do()
和B.Do()
的不同逻辑。
答案 1 :(得分:3)
根据定义,you are satisfying both:
Go类型通过实现该接口的方法来满足接口,仅此而已。此属性允许定义和使用接口,而无需修改现有代码。它支持一种结构类型,可以促进关注点的分离并提高代码的重用率,并且可以更轻松地构建随着代码开发而出现的模式。接口的语义是Go灵活轻巧的主要原因之一。
因此,考虑到这一点,您可以:
a)为定义您对逻辑的期望的接口方法添加注释(参见io.Reader接口或一个好例子)
b)在接口上添加一个名为ImplementsDoerA和ImplementsDoerB的额外方法(也在FAQ中提到)。