How to implement two different interfaces with the same method signature

huangapple go评论81阅读模式
英文:

How to implement two different interfaces with the same method signature

问题

在这种情况下,你可以使用类型转换来解决问题。由于A.DoerB.Doer是两个不同的接口,你需要将C类型转换为这两个接口类型的实现。

main包中,你可以创建两个新的类型,分别实现A.DoerB.Doer接口。然后,在main函数中,你可以将C类型的实例转换为这两个接口类型,并将它们传递给A.FuncAB.FuncB函数。

以下是修改后的代码示例:

package main

import (
	"path/to/A"
	"path/to/B"
)

type C int

// 实现 A.Doer 接口
func (c C) Do() string {
	return "C now implements A"
}

// 实现 B.Doer 接口
func (c C) DoB() string {
	return "C now implements B"
}

func main() {
	c := C(0)

	// 将 C 类型转换为 A.Doer 接口类型
	aDoer := A.Doer(c)
	A.FuncA(aDoer)

	// 将 C 类型转换为 B.Doer 接口类型
	bDoer := B.Doer(c)
	B.FuncB(bDoer)
}

通过这种方式,你可以在不同的包中使用相同的类型实现不同的接口,并且不会出现冲突或错误。

英文:

Suppose that I have to implement two different interfaces declared in two different packages (in two different separated projects).

I have in the package 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
}

And in package 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
}

In my main package

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 !
}

How to deal with this situation ?

答案1

得分: 9

根据常见问题解答中提到的:

通过其他语言的经验,我们发现,具有相同名称但不同签名的多种方法有时很有用,但在实践中可能会令人困惑和脆弱。
在Go的类型系统中,只通过名称匹配并要求类型一致是一个重要的简化决策

在你的情况下,你可以同时满足这两个接口。

你可以通过以下方式测试一个对象(接口类型的对象)是否满足另一个接口类型 A.Doer

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

问题补充:

但是,用于满足 ADo 方法中的逻辑与 B 中的逻辑完全不同。

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

  • DoerA,它将你的对象 C 作为字段,并以满足 A.Do() 的方式实现 A.Do()
  • DoerB,它将相同的对象 C 作为字段,并以满足 B.Do() 的方式实现 B.Do()

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

英文:

As the 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.

In your case, you would satisfy both interfaces.

You can you the can test whether an object (of an interface type) satisfies another interface type A.Doer, by doing:

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

The OP adds:

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

Then you need to implement a wrapper around you object:

  • a DoerA, which has your object C as a field, and implement A.Do() in a manner that satisfy how A.Do()is supposed to work
  • a DoerB, which has your same object C as a field, and implement B.Do() in a manner that satisfy how B.Do() is supposed to work

That way, you will know which Doer to pass to a function expecting an A.Doer or a B.Doer.
You won't have to implement a Do() method on your original object C, which would be unable to cope with the different logic of A.Do() and B.Do().

答案2

得分: 4

根据定义,你同时满足以下两个条件:

  • 通过实现接口的方法,一个Go类型可以满足一个接口的要求,没有其他要求。这个特性使得可以定义和使用接口而无需修改现有的代码。它实现了一种结构化的类型,促进了关注点的分离和代码的重用,使得在代码发展过程中更容易构建出现的模式。接口的语义是Go语言具有灵活、轻量级特性的主要原因之一。

因此,在这个基础上,你可以选择:

a) 在接口方法中添加注释,定义对逻辑的期望(参考io.Reader接口或一个很好的例子)。

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

英文:

By definition, 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.

So, with that in mind, you can either:

a) Add comments to the the interface methods defining your expectations on the logic (see the io.Reader interface or a good example)

b) Add an extra method called ImplementsDoerA and ImplementsDoerB on the interfaces (also mentioned in the FAQ).

huangapple
  • 本文由 发表于 2015年1月24日 18:14:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/28124412.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定