我有some example code我在其中声明一个类型 foo
使用一些相互调用的方法(例如: foo.get
,由 foo.double
和 foo.toString
调用)。
我有另一种类型,bar
, 其中嵌入了 foo
并重新定义 get
.我被迫重新定义 double
和 toString
在 bar
, 所以他们可以看到 bar.get
(而不仅仅是 foo.get
),但这些函数的主体与原始函数基本相同。
有没有更好的方法来组织此代码,以避免冗余,同时仍然有 bar
实现与 foo
相同的接口(interface)?
注意事项:
- 上面组织的代码工作很好;它很难维护,因为当我重新定义最初在
foo
上声明的方法时在嵌入foo
的类型上我必须仔细检查以查看foo
上还有哪些其他方法调用它,并确保重新定义所有这些。事实证明,很容易不小心错过一个。 - 在本文所依据的真实项目中,有近十几种类型嵌入了'
foo
',以及每种类型上相似数量的相互连接的方法。 - 我可以向 foo 添加一个接口(interface)值成员,其中包含指向嵌入它的最外层封闭结构的指针,并具有
foo.double
调用f.outermost.get()
而不是f.get()
, 但这似乎有点浪费并且意味着foo
的零值无效。 (另外,只有当嵌入类型是结构时才有可能。)
最佳答案
在 Go 中有 embedding , 但没有 polymorphism .如果在结构中嵌入类型,则嵌入类型的所有方法都会得到提升,并将位于 method set 中。包装器结构类型。但是您不能“覆盖”提升的方法。当然,您可以添加自己的同名方法,并在包装器结构上调用该名称的方法将调用您的方法,但如果从嵌入式类型调用此方法,则不会将其分派(dispatch)给您的方法,它仍将调用为嵌入类型定义的“原始”方法。
在这里阅读更多相关信息:Does fragile base class issue exist in Go?在这里:Go embedded struct call child method instead parent method .
看起来您只想“继承”double()
和 toString()
方法(应该称为 String()
在 Go 中),但不是 get()
方法,因为它的实现因类型而异。
所以基本上你应该重构一点。您的 foo
类型应该具有/获取一个提供 get()
方法的值。您可以使用 getter
接口(interface)捕获它:
type getter interface {
get() int
}
foo
实现:
type foo struct {
g getter
}
func (f foo) double() int {
return f.g.get() * 2
}
func (f foo) toString() string {
return fmt.Sprintf("%d", f.g.get())
}
还有一个 bar
类型,它嵌入了 foo
,并且只提供“缺失”的 get()
:
type bar struct {
foo
}
func (b bar) get() int {
return 69
}
使用示例:
b := bar{}
b.foo = foo{g: b}
fmt.Println(b.double())
fmt.Println(b.toString())
输出符合预期(在 Go Playground 上尝试):
138
69
用一个简单的函数值
使用上面的 getter
接口(interface)很好,因为它提供了 future 的灵 active ,如果您需要向它添加其他方法。
如果不是这种情况并且您只需要一个函数,则可以省略接口(interface)而只使用一个函数值。
它可能是这样的:
type foo struct {
get func() int
}
func (f foo) double() int {
return f.get() * 2
}
func (f foo) toString() string {
return fmt.Sprintf("%d", f.get())
}
type bar struct {
foo
}
func (b bar) get() int {
return 69
}
并使用它:
b := bar{}
b.foo = foo{get: b.get}
fmt.Println(b.double())
fmt.Println(b.toString())
输出是一样的。在 Go Playground 上试用.
请注意,虽然我们使用了 bar.get()
方法 (b.get
method value ),但并不要求 get()
应该是一个方法。它可以是一个普通的函数值,甚至是一个function literal。 ,例如:
b := bar{foo: foo{get: func() int { return 69 }}}
fmt.Println(b.double())
fmt.Println(b.toString())
在 Go Playground 上试试这个.
关于inheritance - 我如何组织这段从嵌入式类型重新定义方法的 Go 代码,以减少冗余并提高可维护性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44526957/