在Go中创建复杂的结构层次结构的惯用方法是什么?

时间:2015-03-19 12:29:21

标签: inheritance go struct interface embedding

我正在Go中编写一个解释器,我正在寻找存储AST的惯用方法。我读了Go编译器源代码,似乎他们使用带有空方法的接口来表示AST。例如,我们有以下层次结构,

Object
--Immovable
----Building
----Mountain
--Movable
----Car
----Bike

这就是在"空方法"中实现上述层次结构的方法。方式。

type Object interface {
  object()
}

type Immovable interface {
  Object
  immovable()
}

type Building struct {
  ... 
}

type Mountain struct {
  ... 
}

type Movable interface {
  Object
  movable()
}

type Car struct {
  ...
} 

type Mountain struct {
  ...
} 

func (*Building) object() {}
func (*Mountain) object() {}
func (*Car) object() {}
func (*Bike) object() {}
func (*Building) immovable() {}
func (*Mountain) immovable() {}
func (*Car) movable() {}
func (*Bike) movable() {}    

上面的代码是一个人为的例子,这就是Go编译器implemented与几十个空方法的关系。但为什么?请注意定义了多少个空方法。随着层次深度的增加,它可能变得非常复杂。

评论中指出,空方法不允许分配不兼容的类型。在我们的示例中,*Car无法分配给*Immovable

在支持继承的C ++等其他语言中,这很容易。我无法想到代表AST的任何其他方式。

Go编译器AST的实现方式可能是惯用的,但不是直截了当吗?

1 个答案:

答案 0 :(得分:7)

Go是not (quite) an object oriented language:它没有类,而does not have type inheritance;但是它在struct级别和interface级别上支持一个名为 embedding 的类似构造,它确实有methods

Go中的

Interfaces只是固定的方法集。类型隐式实现接口,如果其方法集是接口的超集(没有intent的声明)。

如果您希望文档显式您的类型确实实现了接口(因为没有明确说明),那么空方法很棒。官方Go FAQ: How can I guarantee my type satisfies an interface?

type Fooer interface {
    Foo()
    ImplementsFooer()
}

如果您希望区分类型层次结构(例如,您不希望允许对象同时为MovableImmovable),则它们必须具有不同的方法集(必须在MovableImmovable的每个方法集中至少有一个方法在其他方法中不存在,因为如果方法集包含相同的方法,则执行一个会自动实现另一个,因此您可以将Movable对象分配给Immovable类型的变量。

为具有相同名称的接口添加一个空方法将为您提供这种区别,假设您不会将此类方法添加到其他类型。

减少空方法的数量

我个人对空方法没有任何问题。但是有一种方法可以减少它们。

如果您还为层次结构中的每种类型创建struct 实施,并且每个实现嵌入 struct实现更高一级,更高级别的方法集将自动进入而不会更加轻松:

对象

Object界面和ObjectImpl实施:

type Object interface {
  object()
}
type ObjectImpl struct {}
func (o *ObjectImpl) object() {}

不动产

Immovable界面和ImmovableImpl实施:

type Immovable interface {
    Object
    immovable()
}
type ImmovableImpl struct {
    ObjectImpl // Embed ObjectImpl
}
func (o *Immovable) immovable() {}

注意ImmovableImpl只添加immovable()方法,object()是"继承"。

建筑

Building实施:

type Building struct {
    ImmovableImpl // Embed ImmovableImpl struct

    // Building-specific other fields may come here
}

注意Building 未添加任何新方法,但它自动成为Immovable对象。

如果"子类型数量很多,这种技术的优势会大大增加。增加或者如果界面类型不仅仅有1"标记"方法(因为所有方法都是"继承")。