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

How to implement two different interfaces with the same method signature

假设我必须实现在两个不同的包中声明的两个不同的接口(在两个不同的独立项目中)。

包裹里有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 !
}

如何处理这种情况?

作为 FAQ mentions

Experience with other languages told us that having a variety of methods with the same name but different signatures was occasionally useful but that it could also be confusing and fragile in practice.
Matching only by name and requiring consistency in the types was a major simplifying decision in Go's type system.

在你的情况下,你会满足这两个接口。

你可以测试一个对象(接口类型)是否满足另一个接口类型A.Doer,方法是:

if _, ok := obj.(A.Doer); ok {
}

OP 添加:

But the logic implemented in the Do method to satisfy A is completely different from the one in B.

然后你需要在你的对象周围实现一个包装器:

  • a DoerA,将您的对象 C 作为一个字段,并以满足 A.Do() 应该如何工作的方式实现 A.Do()
  • a DoerB,它具有与字段相同的对象 C,并以满足 B.Do() 应该如何工作的方式实现 B.Do()

这样,您将知道将哪个 Doer 传递给期望 A.DoerB.Doer 的函数。
您不必在原始对象 C 上实现 Do() 方法,这将无法处理 A.Do()B.Do() 的不同逻辑。

根据定义,you are satisfying both:

A Go type satisfies an interface by implementing the methods of that interface, nothing more. This property allows interfaces to be defined and used without having to modify existing code. It enables a kind of structural typing that promotes separation of concerns and improves code re-use, and makes it easier to build on patterns that emerge as the code develops. The semantics of interfaces is one of the main reasons for Go's nimble, lightweight feel.

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

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

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