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

chai2010 · · 4343 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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


有疑问加站长微信联系(非本文作者)

本文来自:开源中国博客

感谢作者:chai2010

查看原文:Go语言中隐式接口的冲突问题

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

4343 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传