Golang 和继承

Posted

技术标签:

【中文标题】Golang 和继承【英文标题】:Golang and inheritance 【发布时间】:2015-11-18 06:02:18 【问题描述】:

我想在我的库中提供一个基础结构,其中包含可以“扩展”的方法。

这个基础结构的方法依赖于扩展结构的方法。 这在 Go 中是不可能直接实现的,因为结构方法只能访问结构自己的字段,而不是父结构。

关键是要有我不必在每个扩展类中重复的功能。

我想出了这个模式,效果很好, 但由于它的循环结构,看起来相当复杂。

我从未在其他 Go 代码中发现过类似的东西。 这是不是很不走运? 我可以采取哪些不同的方法?

type MyInterface interface 
  SomeMethod(string)
  OtherMethod(string)


type Base struct
  B MyInterface


func (b *Base) SomeMethod(x string) 
  b.B.OtherMethod(x)


type Extender struct 
  Base


func (b *Extender) OtherMethod(x string) 
  // Do something...


func NewExtender() *Extender  
  e := Extender
  e.Base.B = &e
  return &e

【问题讨论】:

很难从一个通用的例子中看出你在做什么,但这不是惯用的 Go 代码。通常,您需要避免完全通过类和继承来考虑解决方案。使用组合来发挥自己的优势,并记住并非所有东西都需要是由接口表示的结构——有时函数就是你所需要的。 重新设计您的解决方案。 Go 没有继承。试图用 Go 提供的东西来重构继承很可能会失败。 不明白为什么你被否决了;你问这是否是一种适当的方法,如果不是,你可以做些什么来使它变得更好。冒着看起来像个怪人的风险,我认为你的问题很好。话虽如此,我上面的两个评论者都说对了。 你的代码示例让我很困惑。您可能只想在单个类型上定义“通用方法”并将其嵌入到您想要提供这些方法的类型中。你说你有一个问题'因为结构方法只能访问结构自己的字段',但这不是真的。如果您在 AB 类型中嵌入了某些类型 Base ,那么他们可以直接在其方法中访问 Base 的导出字段。如果 Base 有一个方法 One 并且您想要覆盖它,您可以在 A 中重新定义它,并且仍然像在那些 OO 语言中一样从该上下文中调用 A.Base.One() 我认为你需要给出一个更具体的例子来说明你正在尝试做什么以及为什么你认为这种方法是必要的。我赞成你取消评级,因为我同意@william.taylor.09。 【参考方案1】:

正如人们的 cmets 中提到的,Go 鼓励组合而不是继承。

要解决您关于减少代码重复的问题,您可以使用embedding。

使用上面链接的Effective Go 中的示例,您可以从只做一些事情的非常狭窄的接口开始:

type Reader interface 
    Read(p []byte) (n int, err error)


type Writer interface 
    Write(p []byte) (n int, err error)

然后你可以将接口组合成另一个接口:

// ReadWriter is the interface that combines the Reader and Writer interfaces.
type ReadWriter interface 
    Reader
    Writer

结构体的工作方式类似,您可以在另一个结构体中组合实现 Reader 和 Writer 的结构体:

type MyReader struct 
func (r *MyReader) Read(p []byte) (n int, err error) 
    // Implements Reader interface.

type MyWriter struct 
func (w *MyWriter) Write(p []byte) (n int, err error) 
    // Implements Writer interface.


// MyReadWriter stores pointers to a MyReader and a MyWriter.
// It implements ReadWriter.
type MyReadWriter struct 
    *MyReader
    *MyWriter

基本上,任何实现ReaderWriter 的东西都可以通过将它们组合在一个结构中来重用,并且外部结构将自动实现ReadWriter 接口。

这基本上是在做Dependency Injection,它对测试也超级有用。

以上结构代码示例:

func (rw *MyReadWriter) DoCrazyStuff() 
    data := []byte
    // Do stuff...
    rw.Read(data)
    rw.Write(data)
    // You get the idea...


func main() 
    rw := &MyReadWriter&MyReader, &MyWriter
    rw.DoCrazyStuff()

需要指出的是,与其他语言的组合范式略有不同的是,MyReadWriter 结构现在可以同时充当ReaderWriter。这就是为什么在DoCrazyStuff() 中我们使用rw.Read(data) 而不是rw.Reader.Read(data)

更新:修正了错误的例子。

【讨论】:

除非你几乎不想使用指向接口的指针。您的MyReadWriter 应该使用指向接口的指针,而应该是type MyReadWriter struct Reader, Writer 。这允许(例如)rw := MyReadWriter strings.NewReader("foo"), new(bytes.Buffer) . 感谢您的提醒,很高兴知道这一点。你有解释原因的链接吗?此外,此示例直接来自有效的转到页面。 获取更多接口细节的好地方是a blog article by Russ Cox。 我不得不仔细重新阅读the relevant section 以注意您丢失的上下文。第一个示例显示了io.Readerio.Writer 的定义,它们是接口(还有一个io.ReadWriter)。后者显示*bufio.Reader*bufio.Writer(都是指向结构类型的指针)被嵌入到实现io.ReadWriter 的类型中。这两个摘录来自不同的包,您不能只是将它们剪切粘贴在一起。 @DaveC,你是对的!我用固定的例子更新了答案。【参考方案2】:

很抱歉让您失望了,但您问错了问题。当我开始编写 Go 代码时,我遇到了类似的问题。

您不能简单地将类层次结构转换为 Go 代码,至少不能获得令人满意的结果。通常在 Go 中有一种非常优雅和简单的方法来解决这些问题,但要发现它们,你需要像你习惯的那样思考一些不同的东西。

很遗憾,您的问题并未说明您要解决的什么问题。您刚刚描述了如何您想解决它。因此我有点不愿意给出一个一般性的答案,因为它不会导致惯用的 Go 代码。如果你对这个答案感到失望,我理解,但在我看来,这是你能得到的最有价值的答案:)

【讨论】:

这是评论,不是问题的答案。

以上是关于Golang 和继承的主要内容,如果未能解决你的问题,请参考以下文章

Golang入门到项目实战 | golang继承

(三十)golang--面向对象

Golang OOP继承组合接口

golang继承与接口

Golang之继承,多重继承(struct)

golang中级进阶(二):结构体