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

2024-01-31

我正在用 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 编译器是这样的实施的 https://golang.org/src/go/ast/ast.go具有数十个空方法的 AST。但为什么?请注意定义了多少个空方法。随着层次结构深度的增加,它可能会变得非常复杂。

注释中指出,空方法不允许分配不兼容的类型。在我们的例子中,一个*Car不能分配给*Immovable例如。

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

Go 编译器 AST 的实现方式可能很惯用,但不是更直接吗?


Go is 不是(完全)面向对象的语言 http://golang.org/doc/faq#Is_Go_an_object-oriented_language:它没有类并且它没有类型继承 http://golang.org/doc/faq#inheritance;但它支持类似的构造,称为嵌入都在struct水平和interface水平,并且确实有methods http://golang.org/ref/spec#Method_declarations.

接口 http://golang.org/ref/spec#Interface_types在 Go 中只是固定的方法集。一种隐含地如果其方法集是接口的超集(没有意图声明),则实现接口。

如果你愿意的话,空方法是很好的选择document or 明确说明您的类型确实实现了一个接口(因为没有明确说明)。官方的Go 常见问题解答:如何保证我的类型满足接口? https://golang.org/doc/faq#guarantee_satisfies_interface

type Fooer interface {
    Foo()
    ImplementsFooer()
}

如果您希望类型层次结构有所区别(例如,您不想允许一个对象既是Movable and Immovable),它们必须有不同的方法集(每个方法集中必须至少有 1 个方法Movable and Immovable这在其他方法中不存在),因为如果方法集包含相同的方法,一个方法的实现也会自动实现另一个方法,因此您可以分配一个Movable对象类型的变量Immovable.

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

减少空方法的数量

就我个人而言,我对空方法没有任何问题。不过有一种方法可以减少它们。

如果您还创建了一个struct 执行对于层次结构中的每种类型和每种实现embeds the struct实现高一级,高一级的方法集就会自动来了:

Object

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() {}

Note ImmovableImpl只添加immovable()方法,object()是“继承”的。

Building

Building执行:

type Building struct {
    ImmovableImpl // Embed ImmovableImpl struct

    // Building-specific other fields may come here
}

Note Building 不添加任何新方法,但它会自动成为Immovable object.

如果“子类型”的数量增加或者接口类型具有不止 1 个“标记”方法(因为所有方法都是“继承的”),则该技术的优势会大大增加。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Go 中创建复杂结构层次结构的惯用方法是什么? 的相关文章

随机推荐