繁体   English   中英

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

[英]How can I organise this Go code that redefines methods from an embedded type to be less redundant and more maintainable?

我有一些示例代码 ,其中我声明了一个类型foo ,其中一些方法相互调用(例如: foo.get ,由foo.doublefoo.toString )。

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

是否有更好的方法来组织此代码,以避免冗余,同时仍然bar履行与foo相同的接口?

笔记:

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

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

在这里阅读更多相关内容: Go中是否存在脆弱的基类问题? 在这里: 转到嵌入式struct call子方法而不是父方法

看起来你只想“继承” 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())
}

还有一个嵌入foobar类型,只提供“缺少”的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接口很不错,因为如果你需要为它添加其他方法,它将来会提供灵活性。

如果不是这种情况,并且您只需要一个函数,则可以省略接口并使用函数值。

这是它的样子:

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 方法值 ),但并不要求get()应该是一个方法。 它可以是普通的函数值,甚至是函数文字 ,例如:

b := bar{foo: foo{get: func() int { return 69 }}}
fmt.Println(b.double())
fmt.Println(b.toString())

Go Playground尝试这个。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM