如何组织此Go代码,从嵌入式类型重新定义方法,以减少冗余和更易维护?

时间:2017-06-13 16:28:20

标签: inheritance go methods embedding organization

我有some example code我在其中声明了一个类型foo,其中包含一些相互调用的方法(例如:foo.get,由foo.double和{{1}调用}})。

我有另一种类型foo.toString,它嵌入bar并重新定义foo。我被迫在get上重新定义doubletoString,因此他们可以看到bar(而不仅仅是bar.get),但这些函数的主体是基本上与原版相同。

是否有更好的方法来组织此代码,以避免冗余,同时让foo.get完成与bar相同的接口?

注意:

  • 上面组织的代码正常工作;它很难维护,因为当我重新定义最初在foo上声明的嵌入foo类型的方法时,我必须仔细检查以查看{{1}上的哪些其他方法打电话给它,并确保重新定义所有这些。事实证明,很容易错过一个。
  • 在以此为基础的真实项目中,有近十几种类型嵌入了foo'以及每种类型的相似数量的互连方法。
  • 我可以为foo添加一个接口值成员,其中包含指向嵌入它的最外层封闭结构的指针,并foo调用foo而不是foo.double,但这似乎是有点浪费,并且意味着f.outermost.get()的零值无效。 (另外,只有嵌入类型是结构时才有可能。)

1 个答案:

答案 0 :(得分:3)

在Go中有embedding,但没有polymorphism。如果在结构中嵌入类型,则嵌入类型的所有方法都会被提升,并且将位于包装器结构类型的method set中。但是你无法“覆盖”推广的方法。当然,您可以使用相同的名称添加自己的方法,并在包装​​器结构上调用该名称的方法将调用您的方法,但是如果从嵌入类型调用此方法,则不会将该方法调度到您的方法,仍将调用已定义为嵌入类型的“原始”方法。

在此处详细了解此信息:Does fragile base class issue exist in Go?此处:Go embedded struct call child method instead parent method

看起来您只想“继承”double()toString()方法(在Go中应该称为String()),而不是get()方法它的实现从类型到类型发生了变化。

所以基本上你应该重构一下。您的foo类型应具有/获取提供get()方法的值。您可以使用getter界面捕获此内容:

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的{​​{1}}类型,仅提供“缺失”foo

get()

用法示例:

type bar struct {
    foo
}

func (b bar) get() int {
    return 69
}

输出符合预期(在Go Playground上尝试):

b := bar{}
b.foo = foo{g: b}
fmt.Println(b.double())
fmt.Println(b.toString())

具有简单的功能值

使用上面的138 69 界面很不错,因为如果您需要为其添加其他方法,它将来会提供灵活性。

如果不是这种情况,而您只需要一个功能,则可以省略界面并使用功能值。

这就是它的样子:

getter

使用它:

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
}

输出是一样的。在Go Playground上尝试。

请注意,虽然我们使用了b := bar{} b.foo = foo{get: b.get} fmt.Println(b.double()) fmt.Println(b.toString()) 方法(bar.get() method value),但并不要求b.get应该是一种方法。它可以是普通的函数值,甚至是function literal,例如:

get()

Go Playground上试试这个。