加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

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

发布时间:2020-12-16 18:42:56 所属栏目:大数据 来源:网络整理
导读:Go语言中隐式接口的冲突问题 Go语言中采用的是隐式接口,只要满足的接口的定义,就可以当作接口使用. 比如内置的 error 接口: type error struct {Error() string} 隐式接口的好处有很多. 但我个人觉得最主要的一点就是不需要再去画祖宗八代的继承关系图了(松

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语言简单的编程哲学是矛盾的.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读