• 首页
  • Github
用嵌入字段实现代码重用

对数据结构加锁机制的常用方式,是嵌入一个sync.Mutex或sync.RWMutex。

type Foo struct {
  sync.Mutex
  // ... other fields
}

f := new(Foo)
f.Lock()
// ... operations
f.Unlock()

Foo嵌入了sync.Mutex,所以可以在Foo结构上直接调用Lock和Unlock方法。 但这不是个仅限于此的语法糖,Foo实际已经实现了sync.Locker接口,就好像是Foo自己定义了Lock和Unlock方法一样。 Foo结构可以用于任何需要sync.Locker的地方,例如

condition := sync.NewCond(new(Foo))

这就类似于接口的默认实现(default implementation)。实现接口时,只需要嵌入一个实现了此接口的类型, 而不需要重新实现一遍方法,或者使用命名字段再包装出接口所需的方法。

当然这个和真正的默认实现机制是有区别的,方法里如果需要引用被嵌入的类型的字段,须要在构造嵌入字段时显式传入。 例如这个接口

type Named interface {
	PrintName()
}

要提供一个Named接口的可以嵌入的实现NamedImpl,可以有以下的方法。其一

type Named interface {
	PrintName()
	GetName() string
}

type NamedImpl struct {
	named Named
}

func (n NamedImpl) PrintName() {
	fmt.Printf("name is %s\n", n.named.GetName())
}

type Foo struct {
	NamedImpl
	Name string
}

func (f Foo) GetName() string {
	return f.Name
}

func main() {
	var f Foo
	f.Name = "foo"
	f.NamedImpl = NamedImpl{f}
	f.PrintName()
}

给Named接口增加一个GetName方法。虽然感觉有点多余但也算可行。

其二,不增加接口方法,只传需要的数据的引用

type Named interface {
	PrintName()
}

type NamedImpl struct {
	name *string
}

func (n NamedImpl) PrintName() {
	fmt.Printf("name is %s\n", *n.name)
}

type Foo struct {
	NamedImpl
	Name string
}

func main() {
  var f Foo
  f.Name = "foo"
  f.NamedImpl = NamedImpl{&f.Name}
  f.PrintName()
}

其三,不使用引用,而是嵌入一个closure以获得所需数据

type Named interface {
	PrintName()
}

type NamedImpl func() string

func (n NamedImpl) PrintName() {
	fmt.Printf("name is %s\n", n())
}

type Foo struct {
	NamedImpl
	Name string
}

func main() {
	var f Foo
	f.Name = "foo"
	f.NamedImpl = func() string { return f.Name }
	f.PrintName()
}

以上例子中,其他类型要实现Named接口,可以不实现PrintName方法,而是嵌入一个NamedImpl。

func main() {
	b := struct {
		NamedImpl
		name string
	}{}
	b.name = "bar"
	b.NamedImpl = func() string { return b.name }
	b.PrintName()
}

这就实现了代码的重用。

2014-08-28
comments powered by Disqus