Go语言中的隐式接口冲突 问题
Go中隐式接口,只要实现了某个接口就能当作那个接口使用,但是在不同接口中,可能有相同名字的方法,这时候就有可能会有接口冲突的问题。
隐式接口的好处之一就是松耦合,接口之间容易相互转换
比如有个自定义接口 MyError 接口,里面有个Error()方法
type MyError interface {
Error() string
}
目前这个 MyError 就与Go中内置的 error 接口冲突了,因为都只有一个 Error() 方法
type error interface {
Error() string
}
这样的话因为隐式接口的原因,这两个接口就是等价的,只要实现了 Error() 方法,那么既是 MyError类型也是 error 类型
如果要把这两个接口区分开的话,也就是让这两个接口不等价
那么可以选择MyError中加一个 唯一的空方法 来区分两个接口
type MyError interface {
Error() string
ProtoMyError()
}
以下是个人理解:
这里MyError 接口有两个方法,其中的 空方法 是为了区分 error 接口,当一个结构体实现了 MyError中的两个方法时,那么它既是MyError类型也是Error类型,但是
如果只实现了 Error() 方法时,那么它就只是 error类型,而不是MyError类型。
添加 唯一空方法 是Protobuf 中的 proto.Message 所使用的办法
// Message is implemented by generated protocol buffer messages.
type Message interface {
Reset()
String() string
ProtoMessage()
}
生成的每个 Message 类型有个特殊的 ProtoMessage 空方法, 特别对应 proto.Message 接口.
极端的做法是随机生成一个 特别的 方法名, 比如用 UUID 做唯一名字.
但是, 公开的名字依然有被别人恶意覆盖的危险(实际中不大可能).
更严格的做法是将这个用于区别接口的方法名定义为私有的方法. 比如 testing.TB:
type TB interface {
Error(args ...interface{})
Errorf(format string, args ...interface{})
Fail()
FailNow()
Failed() bool
Fatal(args ...interface{})
Fatalf(format string, args ...interface{})
Log(args ...interface{})
Logf(format string, args ...interface{})
Skip(args ...interface{})
SkipNow()
Skipf(format string, args ...interface{})
Skipped() bool
// A private method to prevent users implementing the
// interface and so future additions to it will not
// violate Go 1 compatibility.
private()
}
private 不仅仅是私有方法, 而且必须是 testing 包内部定义的 private() 方法的类型才能匹配这个接口!
因此 testing.TB 接口是全局唯一的, 不会出现等价可互换的接口.
现在 testing.TB 保证了接口的唯一性, 但是如何在外部实现 这个接口呢(private()是testing 包内部定义的)?
我们可以从 testing.TB 接口继承这个 private() 方法:
package main
import (
"fmt"
"testing"
)
type TB struct {
testing.TB
}
func (p *TB) Fatal(args ...interface{}) {
fmt.Println("TB.Fatal disabled!")
}
func main() {
var tb testing.TB = new(TB)
tb.Fatal("Hello, playground")
}
play 地址: http://play.golang.org/p/tFB0fLwq9q
上面的代码模拟了显式接口, 而且 testing.TB 接口永远不用担心有冲突的危险.
参考文章:?