嵌入类型会泄漏实现细节、禁止类型演化和产生模糊的接口文档。

假设您使用共享的 AbstractList 实现了多种列表类型,请避免在具体的列表实现中嵌入 AbstractList。相反,只需手动将方法写入具体的列表,委托给抽象列表的方法。

type AbstractList struct {}
// Add 将实体添加到列表中。
func (l *AbstractList) Add(e Entity) {
    // ...
}
// Remove 从列表中移除实体。
func (l *AbstractList) Remove(e Entity) {
    // ...
}

// Bad

// ConcreteList 是一个实体列表。
type ConcreteList struct {
    *AbstractList
}

// Good

// ConcreteList 是一个实体列表。
type ConcreteList struct {
    list *AbstractList
}
// Add 将实体添加到列表中。
func (l *ConcreteList) Add(e Entity) {
    l.list.Add(e)
}
// Remove 从列表中移除实体。
func (l *ConcreteList) Remove(e Entity) {
    l.list.Remove(e)
}

泄漏实现细节指 AbstractList 的实现是 ConcreteList 的实现细节,被导出泄露了;

禁止类型演化指 ConcreteList 获得了同名嵌套类型字段 AbstractList,如果嵌入的类型是 public,那么字段是 public。为了保持向后兼容性,外部类型的每个未来版本都必须保留嵌入类型;

产生模糊的接口文档指 AbstractList 被导出的字段和方法全部成为了 ConcreteList 被导出的字段和方法,在 ConcreteList 又没有明确说明,会产生模糊的接口文档。

很少需要嵌入类型,虽然它可以帮助您避免编写冗长的委托方法。

即使嵌入兼容的抽象列表 interface,而不是结构体,这将为开发人员提供更大的灵活性来改变未来,但仍然泄露了具体列表使用抽象实现的细节。

// Bad

// AbstractList 是各种实体列表的通用实现。
type AbstractList interface {
    Add(Entity)
    Remove(Entity)
}
// ConcreteList 是一个实体列表。
type ConcreteList struct {
    AbstractList
}

// Good

// ConcreteList 是一个实体列表。
type ConcreteList struct {
    list AbstractList
}
// 添加将实体添加到列表中。
func (l *ConcreteList) Add(e Entity) {
    l.list.Add(e)
}
// 移除从列表中移除实体。
func (l *ConcreteList) Remove(e Entity) {
    l.list.Remove(e)
}

无论是使用嵌入结构还是嵌入接口,都会限制类型的演化。

(1)向嵌入接口添加方法是一个破坏性的改变;

(2)从嵌入结构体删除方法是一个破坏性的改变;

(3)删除嵌入类型是一个破坏性的改变;

(4)即使使用满足相同接口的类型替换嵌入类型,也是一个破坏性的改变。

尽管编写这些委托方法是乏味的,但是额外的工作隐藏了实现细节,留下了更多的更改机会,还消除了在未能描述出潜在接口的模糊文档。

powered by Gitbook该文章修订时间: 2024-03-22 15:30:00

results matching ""

    No results matching ""