2009-11-13 107 views

回答

29

在评论中,你想知道嵌入的想法是否足以“完全替代继承”。我会说这个问题的答案是“是”。几年前,我用一个名为Snit的Tcl OO系统进行了非常简短的演示,该系统使用组合和委派来排除继承。Snit仍然与Go的方法大不相同,但从这个方面来说,他们有一些共同的哲学基础。这是一种将功能和责任组合在一起的机制,而不是类的层次结构。正如其他人所说的,这实际上是语言设计者希望支持哪种编程实践。所有这些选择都有自己的利弊。我不认为“最佳实践”是一个必然适用于此的短语。我们可能会看到有人为Go最终开发一个继承层。

(对于任何熟悉Tcl的读者,我觉得Snit与[incr Tcl]的语言“感觉”稍微接近一点.Tcl完全是关于代表团的,至少对我来说是这样。)

36

Gang of 4的关键原则是 “宁可组成继承”;去使得你跟着它;-)。

+4

继承是过度使用,我很欣赏转到如何简化组成,但我真的很想知道的问题是嵌入是否可以完全替代继承。我想这是一个难以回答的问题,而不需要真正去编写代码 – Casebash 2009-11-13 05:32:51

+0

嗯,你不(直接)得到一些关键的继承 - 铰链设计模式,比如模板方法,但这似乎不是一个杀手 - 在最坏的情况下,这似乎会导致某些便利性的丧失(需要稍微更明确的编码)。 – 2009-11-22 19:08:13

+0

@Casebash:人们已经能够用JS原型去了,我们可以说它只是一种构图而已。 – 2013-09-17 05:51:24

12

继承的唯一真正用途是:

  • 多态性

    • Go的界面的 “静态鸭打字” 系统解决了这个问题从另一个类

  • 借款实施

    • 这是嵌入是

Go的方法不准确映射1对1,考虑继承和多态在Java中这个经典的例子(based on this):

//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; 
    } 
} 

在这里,继承和多态是结合在一起的,不能在不改变底层结构的情况下将其转换为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 
} 

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

+0

您提到这不会编译,只有几点可以帮助其他人阅读: 类型需要Go中的类型字面量。使用'type RegularAccount struct {}'而不是'Type RegularAccount {}' 您不能在类型定义中放入func原型。在类型之外使用接收方语法:'func(this * receiverType)funcName(parms)returnType' 您必须为返回值的funcs提供返回类型。 'func(account * RegularAccount)maxWithdraw()int {}' 最后,在Go中需要使用左大括号结束“func”行,而不是将它放在自己的行上。 – burfl 2013-03-05 14:14:54

+0

我试图将此作为一项练习来写作 - 在Go的早期阶段我已经接近完成了,并且如果有更多经验可以嵌入并纠正/完成它,我会非常感激它吗? https://gist.github.com/mindplay-dk/807179beda57e676b8fb – 2013-12-03 04:37:01

3

我现在刚刚了解Go,但既然您要求提供意见,我会根据迄今为止所了解的情况提供一个意见。嵌入似乎是Go中许多其他功能的典型特征,它是对现有语言已经完成的最佳实践的明确语言支持。例如,正如Alex Martelli指出的那样,4人帮会说“更喜欢组合来继承”。 Go不仅可以删除继承,而且使组合更容易,更强大,比C++/Java/C#更强大。

我一直困惑于诸如“Go提供了什么新东西,我已经不能用语言X做”,以及“为什么我们需要另一种语言?”等评论。在我看来,从某种意义上说,Go没有提供任何以前无法完成的新功能,但从另一方面来说,Go的新功能是促进和鼓励使用最好的技术已经在实践中使用其他语言。

+3

在某些方面,Go中的新功能已被拿走 - 这是新语言的关键原因。如果他们只是增加功能,它可能是C+++;)但是为了夺取功能(继承,指针算术,手动内存分配)需要一种新语言。 – 2009-11-20 17:47:28

3

人们请求链接到关于嵌入到Go的信息。

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

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

的例子更有意义,当你已经有了围棋接口和类型把握好,但是你可以通过一个接口的思想作为名字的一套方法,如果伪造它,你想到的一个类似于C结构的结构。

有关结构的更多信息,你可以看到Go语言规范,其中明确提到了结构的无名成员为嵌入式类型:

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

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

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

7

嵌入提供自动代表团。这本身并不足以替代继承,因为嵌入不提供任何形式的多态。 Go接口确实提供了多态性,它们与您可能使用的接口有些不同(有些人把它们比作鸭子打字或结构打字)。

在其他语言中,继承层次结构需要仔细设计,因为更改广泛,因此很难做到。 Go避免了这些缺陷,同时提供了一个强大的选择。

下面是深入到OOP一起去多一点的文章:http://nathany.com/good

3

我喜欢它。

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

继承,你必须建立 - 从抽象的东西开始,然后将其子类化为具体细节。你的实际有用的代码将被埋在N级深层。这使得很难使用对象的“部分”,因为如果不在父类中拖动,就无法重新使用代码。

在Go中,您可以通过这种方式为您的类建模(使用接口)。但是你没有(不能)用这种方式编码。

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

在转,这不断迫使程序员思考模块化。这导致程序具有低耦合。低耦合使维护更容易。 (“哦,你看,Duck.quack()确实是长期和复杂的,但至少我知道,它不依赖于鸭的其余部分。”)