嵌入类型(比如互斥锁)应该位于结构体的字段列表的顶部(第一个),并且嵌入字段和常规字段之间必须有一个空行予以区分。
Bad
type Client struct {
version int
http.Client
}
Good
type Client struct {
//嵌入类型位于结构体字段列表的顶部
http.Client
//空行分割
version int
}
嵌入应该提供切实的好处,比如以语义合适的方式添加或扩充功能。这样做应该不会对用户产生不利影响(另外请查阅 Avoid Embedding Types in Public Structs)
如果有以下几点,就不应该使用嵌入:
- 纯粹是为了美观或方便。
- 使外部类型更难于构造或使用。
- 影响外部类型的零值。如果外部类型有一个有用的零值,那么在嵌入内部类型之后,它仍然应该有一个有用的零值
- 作为嵌入内部类型的副作用,从外部类型公开不相关的函数或字段。
- 暴露了未导出类型。
- 影响外部类型的复制语义。
- 改变了外部类型的API或类型语义
- 嵌入了一个非规范形式的内部类型。
- 暴露了外部类型的实现细节。
- 允许用户观察或控制内部组件类型。
- 通过包装来改变内部函数的一般行为,这种包装方式一定会让用户感到疑惑。
简单地说,要有意识地目的明确地去嵌入。一个好的测试技巧是,“是否所有的这些(内嵌类型)导出的内部方法/字段,都能够被直接添加到外部类型中”;如果答案是"some"或"no",那么不要嵌入内部类型,而是使用一个字段来代替。
Bad
//可以使用 A.Lock() 和 A.Unlock(),没有提供任何功能上的好处,并且还能够允许
//用户去控制A的内部的详细细节
type A struct {
// Bad: A.Lock() and A.Unlock() are
// now available, provide no
// functional benefit, and allow
// users to control details about
// the internals of A.
sync.Mutex
}
Good
//为了特定的目的,在外层提供了Write()方法,并将具体实现委托给内部类型WriteCloser的Write()方法
type countingWriteCloser struct {
// Good: Write() is provided at this
// outer layer for a specific
// purpose, and delegates work
// to the inner type's Write().
io.WriteCloser
count int
}
func (w *countingWriteCloser) Write(bs []byte) (int, error) {
w.count += len(bs)
return w.WriteCloser.Write(bs)
}
Bad
type Book struct {
// Bad: pointer changes zero value usefulness
io.ReadWriter
// other fields
}
// later
//外部类型应该还是有一个有用的零值
var b Book
b.Read(...) // panic: nil pointer
b.String() // panic: nil pointer
b.Write(...) // panic: nil pointer
Good
type Book struct {
// Good: has useful zero value
bytes.Buffer
// other fields
}
// later
var b Book
b.Read(...) // ok
b.String() // ok
b.Write(...) // ok
Bad
type Client struct {
//所有的这些导出的内嵌类型的内部方法/字段,都能够被直接添加到外部类型中了
sync.Mutex
sync.WaitGroup
bytes.Buffer
url.URL
}
Good
type Client struct {
mtx sync.Mutex
wg sync.WaitGroup
buf bytes.Buffer
url url.URL
}