如何使用相同的方法签名实现两个不同的接口

时间:2015-01-24 10:14:45

标签: interface go

假设我必须在两个不同的包中实现两个不同的接口(在两个不同的分离项目中)。

我在包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 !
}

如何应对这种情况?

2 个答案:

答案 0 :(得分:7)

作为FAQ mentions

  

使用其他语言的经验告诉我们,使用同名但不同签名的各种方法偶尔会有用,但在实践中也可能会令人困惑和脆弱。
  仅按名称进行匹配并要求在类型中保持一致性是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.DoerB.Doer的函数。
您不必在原始对象Do()上实施C方法,这将无法应对A.Do()B.Do()的不同逻辑。

答案 1 :(得分:3)

根据定义,you are satisfying both

  

Go类型通过实现该接口的方法来满足接口,仅此而已。此属性允许定义和使用接口,而无需修改现有代码。它支持一种结构类型,可以促进关注点的分离并提高代码的重用率,并且可以更轻松地构建随着代码开发而出现的模式。接口的语义是Go灵活轻巧的主要原因之一。

因此,考虑到这一点,您可以:

a)为定义您对逻辑的期望的接口方法添加注释(参见io.Reader接口或一个好例子)

b)在接口上添加一个名为ImplementsDoerA和ImplementsDoerB的额外方法(也在FAQ中提到)。