Go语言中隐式接口的冲突问题

Go语言中隐式接口的冲突问题

Go语言中采用的是隐式接口, 只要满足的接口的定义, 就可以当作接口使用.

比如内置的 error 接口:

type error struct {
	Error() string
}

隐式接口的好处有很多. 但我个人觉得最主要的一点就是不需要再去画祖宗八代的继承关系图了(松耦合).

但是隐式接口会带来冲突问题.

简单来说, 我也想定义一个自己的 MyError 接口, 里面也有一个 Error() string 方法:

type MyError struct {
	Error() string
}

但是我希望 MyError 接口 和 error 接口 是不同的类型 (不能相互转换).

当然, 在 Go语言中 MyError 接口 和 error 接口 是等价的, 禁止 相互转换 比较困难.

我们一般可以在 MyError 接口 中增加一个唯一的空方法 来回避这个问题:

type MyError struct {
	Error() string
	AssertMyError()
}

方法 AssertMyError 只是为了区别 error 接口, 没有其他用处.

这是 Protobuf 中 proto.Message 采用的方法:

// Message is implemented by generated protocol buffer messages.
type Message interface {
	Reset()
	String() string
	ProtoMessage()
}

生成的每个 Message 类型有个特殊的 ProtoMessage 空方法, 特别对应 proto.Message 接口.

当然, 如果有另一个接口刚好也有 ProtoMessage 方法, 还是有冲突的危险.

极端的做法是随机生成一个 特别的 方法名, 比如用 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 接口永远不用担心有冲突的危险.

当然, 上面的代码有过度使用技巧的问题, 这和Go语言简单的编程哲学是矛盾的.

转载于:https://my.oschina.net/chai2010/blog/416679

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值