inheritance - 我如何组织这段从嵌入式类型重新定义方法的 Go 代码,以减少冗余并提高可维护性?

标签 inheritance go methods embedding organization

我有some example code我在其中声明一个类型 foo使用一些相互调用的方法(例如: foo.get ,由 foo.doublefoo.toString 调用)。

我有另一种类型,bar , 其中嵌入了 foo并重新定义 get .我被迫重新定义 doubletoStringbar , 所以他们可以看到 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/

相关文章:

swift - 将 swift 中的对象层次结构映射到 firebase

go - kubernetes API中是否有通过注解获取所有服务的函数

javascript - 在 IE8 中恢复被覆盖的 JavaScript 方法

java - 调用方法时进行装箱

c++ - 为什么 C++ 编译器不区分同名的继承公共(public)方法和继承私有(private)方法?

c++:继承具有不同(但协变)返回类型的方法

go - 访问嵌入式结构值时出现索引超出范围错误

ios - Objective-C 的调用图或控制流图(iOS 应用程序)

c# - 如何隐藏派生类的扩展方法?

go - 未能在 key 中找到任何 PEM 数据