Go 中创建复杂结构层次结构的惯用方法是什么?
What is the idiomatic way in Go to create a complex hierarchy of structs?
我正在用 Go 编写一个解释器,我正在寻找存储 AST 的惯用方式。我阅读了 Go 编译器源代码,似乎他们使用带有空方法的接口来表示 AST。例如,我们有以下层次结构,
Object
--Immovable
----Building
----Mountain
--Movable
----Car
----Bike
这就是上述层次结构在 "empty method" 中的实现方式。
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 AST 与许多空方法的方式。但为什么?注意定义了多少个空方法。随着层级深度的增加,它可能会变得非常复杂。
评论中指出,空方法不允许分配不兼容的类型。例如,在我们的示例中,*Car
不能分配给 *Immovable
。
这在其他支持继承的语言(如 C++)中非常容易。我想不出任何其他表示 AST 的方法。
Go 编译器 AST 的实现方式可能是惯用的,但不是那么直接吗?
Go 是 not (quite) an object oriented language: it does not have classes and it does not have type inheritance; but it supports a similar construct called embedding both on struct
level and on interface
level, and it does have methods.
Go 中的 Interfaces 只是固定的方法集。如果类型的方法集是接口的超集(没有意图声明),则类型 隐式 实现接口。
如果您想记录或明确声明您的类型确实实现了接口(因为没有说明明确)。官方Go FAQ: How can I guarantee my type satisfies an interface?
type Fooer interface {
Foo()
ImplementsFooer()
}
如果你想在你的类型层次结构中有所区别(例如,你不想让一个对象既是 Movable
又是 Immovable
),它们必须有不同的方法集(必须有Movable
和 Immovable
的每个方法集中至少有 1 个方法不存在于另一个方法集中),因为如果方法集包含相同的方法,其中一个的实现将自动实现另一个也因此您可以将 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()
是"inherited".
建筑物
Building
实施:
type Building struct {
ImmovableImpl // Embed ImmovableImpl struct
// Building-specific other fields may come here
}
注意Building
不添加任何新方法,但它自动成为一个Immovable
对象。
如果 "subtypes" 的数量增加,或者如果接口类型有不止 1 个 "marker" 方法(因为所有方法都是 "inherited"),这种技术的优势会大大增加。
我正在用 Go 编写一个解释器,我正在寻找存储 AST 的惯用方式。我阅读了 Go 编译器源代码,似乎他们使用带有空方法的接口来表示 AST。例如,我们有以下层次结构,
Object
--Immovable
----Building
----Mountain
--Movable
----Car
----Bike
这就是上述层次结构在 "empty method" 中的实现方式。
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 AST 与许多空方法的方式。但为什么?注意定义了多少个空方法。随着层级深度的增加,它可能会变得非常复杂。
评论中指出,空方法不允许分配不兼容的类型。例如,在我们的示例中,*Car
不能分配给 *Immovable
。
这在其他支持继承的语言(如 C++)中非常容易。我想不出任何其他表示 AST 的方法。
Go 编译器 AST 的实现方式可能是惯用的,但不是那么直接吗?
Go 是 not (quite) an object oriented language: it does not have classes and it does not have type inheritance; but it supports a similar construct called embedding both on struct
level and on interface
level, and it does have methods.
Interfaces 只是固定的方法集。如果类型的方法集是接口的超集(没有意图声明),则类型 隐式 实现接口。
如果您想记录或明确声明您的类型确实实现了接口(因为没有说明明确)。官方Go FAQ: How can I guarantee my type satisfies an interface?
type Fooer interface {
Foo()
ImplementsFooer()
}
如果你想在你的类型层次结构中有所区别(例如,你不想让一个对象既是 Movable
又是 Immovable
),它们必须有不同的方法集(必须有Movable
和 Immovable
的每个方法集中至少有 1 个方法不存在于另一个方法集中),因为如果方法集包含相同的方法,其中一个的实现将自动实现另一个也因此您可以将 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()
是"inherited".
建筑物
Building
实施:
type Building struct {
ImmovableImpl // Embed ImmovableImpl struct
// Building-specific other fields may come here
}
注意Building
不添加任何新方法,但它自动成为一个Immovable
对象。
如果 "subtypes" 的数量增加,或者如果接口类型有不止 1 个 "marker" 方法(因为所有方法都是 "inherited"),这种技术的优势会大大增加。