小编典典

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

go

我在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编译器如何使用许多空方法来实现
AST的方式。但为什么?注意定义了多少个空方法。随着层次结构深度的增加,它可能会变得非常复杂。

注释中指出,空方法禁止分配不兼容的类型。在我们的示例中,*Car不能将a分配给*Immovable例如。

在其他支持继承的语言(例如C ++)中,这是如此容易。我想不出代表AST的任何其他方式。

Go编译器AST的实现方式可能是惯用的,但不是那么简单吗?


阅读 272

收藏
2020-07-02

共1个答案

小编典典

Go 不是(相当)一种面向对象的语言:它没有类,也没有类型继承。但它支持类似的构造,称为在层和层上
嵌入
,并且确实具有方法struct``interface

Go中的接口只是固定的方法集。如果类型的方法集是该接口的超集(没有意图的声明),则该类型
隐式 实现接口。

如果要 明确 记录声明
您的类型确实实现了接口(因为未明确声明),则空方法非常有用。官方Go常见问题解答:如何保证我的类型满足界面要求?

type Fooer interface {
    Foo()
    ImplementsFooer()
}

如果你想在你的类型层次区分(例如,你不想让一个对象既MovableImmovable),它们必须有不同的方法集(必须有至少1方法在每个方法集的MovableImmovable是不存在于另一个Movable变量中),因为如果方法集将包含相同的方法,那么一个方法的实现也会自动实现另一个方法,因此您可以将一个对象分配给type变量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对象。

如果“子类型”的数量增加或接口类型具有不止一种“标记”方法(因为“继承”了所有方法),则该技术的优势将大大增加。

2020-07-02