在Go中embedded而不是inheritance

你对这个devise决定有什么看法? 它有什么优点和缺点?

链接:

  • embedded描述

在一个评论中,你想知道embedded的想法是否足以“完全替代inheritance”。 我会说这个问题的答案是“是”。 几年前,我用一个名为Snit的Tcl OO系统进行了非常简短的演示 ,它使用了组合和委派来排除inheritance。 Snit与Go的方法仍然有很大的不同,但是在这方面他们有一些共同的哲学基础。 这是一个将function和责任连接在一起的机制,而不是类的层次结构。

正如其他人所说,这实际上是语言devise者想要支持的编程实践。 所有这些select都有自己的利弊。 我不认为“最佳实践”是一个必然适用于此的短语。 我们可能会看到有人为Go最终开发一个inheritance层。

(对于任何熟悉Tcl的读者,我觉得Snit比语言的“感觉”稍微接近一点,Tcl就是关于代表团,至less在我看来是这样。)

“ 四人帮 ”的关键原则是“更愿意inheritance”。 去你跟着它;-)。

inheritance的唯一真正用途是:

  • 多态性

    • Go界面的“静态鸭子打字”系统解决了这个问题
  • 借用另一个课程的实施

    • 这是embedded的目的

Go的方法并不是完全映射到1,请考虑Java中的inheritance和多态的经典示例( 基于此 ):

 //roughly in Java (omitting lots of irrelevant details) //WARNING: don't use at all, not even as a test abstract class BankAccount { int balance; //in cents void Deposit(int money) { balance += money; } void withdraw(int money) { if(money > maxAllowedWithdrawl()) throw new NotEnoughMoneyException(); balance -= money; } abstract int maxAllowedWithdrawl(); } class Account extends BankAccount { int maxAllowedWithdrawl() { return balance; } } class OverdraftAccount extends BankAccount { int overdraft; //amount of negative money allowed int maxAllowedWithdrawl() { return balance + overdraft; } } 

在这里,inheritance和多态是结合在一起的,你不能在不改变底层结构的情况下把它转换成Go。

我还没有深入研究Go,但我想这应该是这样的:

 //roughly Go? .... no? //for illustrative purposes only; not likely to compile // //WARNING: This is totally wrong; it's programming Java in Go type Account interface { AddToBalance(int) MaxWithdraw() int } func Deposit(account Account, amount int) { account.AddToBalance(amount) } func Withdraw(account Account, amount int) error { if account.MaxWithdraw() < amount { return errors.New("Overdraft!") } account.AddToBalance(-amount) return nil } type BankAccount { balance int } func (account *BankAccount) AddToBalance(amount int) { account.balance += amount; } type RegularAccount { *BankAccount } func (account *RegularAccount) MaxWithdraw() int { return account.balance //assuming it's allowed } type OverdraftAccount { *BankAccount overdraft int } func (account *OverdraftAccount) MaxWithdraw() int { return account.balance + account.overdraft } 

按照说明,这是完全错误的编码方式,因为在Go中执行Java。 如果一个人在Go上写这样的东西,可能会组织起来和这个有很大的不同。

embedded提供自动委派。 这本身并不足以取代inheritance,因为embedded不提供任何forms的多态。 Go接口确实提供了多态性,它们与您可能使用的接口有些不同(有些人把它们比作鸭子打字或结构打字)。

在其他语言中,inheritance层次结构需要仔细devise,因为变化是广泛的,因此很难做到。 去避免这些陷阱,同时提供一个强大的select。

这里有一篇文章深入研究OOP: http : //nathany.com/good

我现在刚刚了解Go,但是由于您在征求意见,所以我会根据我所知道的内容提供一个意见。 在Go中,embedded似乎是很多其他事物的典型特征,这是对现有语言中已经完成的最佳实践的明确的语言支持。 例如,正如亚历克斯·马尔泰利(Alex Martelli)所指出的那样,四人帮说“更喜欢inheritance”。 不仅去除inheritance,而且使得组合更容易和更强大的比在C + + / Java / C#中。

我一直困惑的评论,如“提供什么新的东西,我已经不能用语言X,”和“为什么我们需要另一种语言? 在我看来,从某种意义上来说,Go并没有提供任何新的事情,但是从另一个angular度来说,Go的新function是促进和鼓励使用最好的技术已经在实践中使用其他语言。

人们要求链接到关于embedded到Go的信息。

下面是一个“Effective Go”文档,其中讨论了embedded,并提供了具体的例子。

http://golang.org/doc/effective_go.html#embedding

当你已经对Go的接口和types已经有了很好的把握的时候,这个例子会更有意义,但是你可以通过把接口想象成一组方法的名字来伪装它,如果你认为一个类似于C结构的结构。

有关结构的更多信息,您可以看到Go语言规范,它将结构的无名成员显式提及为embeddedtypes:

http://golang.org/ref/spec#Struct_types

到目前为止,我只用它作为一个方便的方法,把一个结构放在另一个结构中,而不必使用内部结构的字段名称,当一个字段名称不会添加任何值的源代码。 在下面的编程练习中,我将一个提案types捆绑在具有提案和响应通道的types中。

https://github.com/ecashin/go-getting/blob/master/bpaxos.go#L30

我喜欢。

您使用的语言会影响您的思维模式。 (只要求一个C程序员实现“字数”,他们可能会使用一个链表,然后切换到二叉树来performance,但是每个Java / Ruby / Python程序员都会使用Dictionary / Hash。大脑,以至于无法想到使用任何其他数据结构。)

通过inheritance,你必须build立一个抽象的东西,然后将其子类化为具体的东西。 你的实际有用的代码将被埋在一个N级深层。 这使得很难使用对象的“部分”,因为无需在父类中拖动就不能重新使用代码。

在Go中,你可以用这种方法来“build模”你的类(使用接口)。 但是你不(不能)用这种方式编码。

相反,你可以使用embedded。 你的代码可以分解成小的,隔离的模块,每个模块都有自己的数据。 这使得重新使用琐碎。 这种模块化与你的“大”对象无关。 (例如,在Go中,你可以写一个甚至不知道你的Duck类的“quack()”方法,但是在一个典型的OOP语言中,你不能声明“我的Duck.quack()实现不依赖于鸭子的其他方法“)

在Go中,这不断迫使程序员去思考模块化问题。 这导致程序具有低耦合。 低耦合使维护更容易。 (“哦,看,Duck.quack()真的很长很复杂,但至less我知道它不依赖于鸭子的其余部分。”)