您对这个设计决定有何看法?它具有什么优势以及哪些缺点?

链接:

有帮助吗?

解决方案

在评论中,您想知道嵌入想法是否足以“完全替换继承”。我会说这个问题的答案是“是”。几年前,我用TCL OO系统进行了非常简短的演奏 发怒, ,使用构图和授权来排除继承。 SNIT仍然与Go的方法大不相同,但是在这方面,他们有一些共同的哲学基础。这是将功能和责任部门结合在一起的机制,而不是课堂的层次结构。

正如其他人所说,这实际上是关于语言设计师想要支持的哪种编程实践。所有这些选择都带有自己的利弊。我认为“最佳实践”不一定是在这里适用的短语。我们可能会看到某人最终会开发一个继承层。

(对于任何熟悉TCL的读者,我都觉得SNIT与语言的“感觉”略微匹配,而不是 [incr Tcl] 曾是。 TCL至少与我的思维方式有关。)

其他提示

团伙4的关键原则是“比继承更喜欢组成”;去 制作 您跟随它;-)。

继承的唯一真正用途是:

  • 多态性

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

    • 这就是嵌入的目的

GO的方法不完全映射1比1,考虑了Java中继承和多态性的经典示例(基于此):

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

在这里,结合了继承和多态性,您不能在不改变基础结构的情况下将其转换为进行。

我还没有深入研究,但我想看起来像这样:

//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中写这样的东西,它可能会与此组织有很大的不同。

嵌入提供自动委托。这本身不足以取代继承,因为嵌入没有提供多态性的形式。 GO界面确实提供了多态性,它们与您可能使用的接口有点不同(有些人将它们比作鸭打字或结构性打字)。

在其他语言中,需要仔细设计继承层次结构,因为变化很广泛,因此很难做到。 Go在提供强大的替代方案的同时避免了这些陷阱。

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

我刚刚了解GO,但是由于您要求提出意见,因此我会根据到目前为止所知道的。嵌入似乎是GO中许多其他事物的典型代表,这是对现有语言已经完成的最佳实践的明确语言支持。例如,正如亚历克斯·马特利(Alex Martelli)指出的那样,4的帮派说“更喜欢构图而不是继承”。 GO不仅可以删除继承,而且比C ++/Java/c#中的组成更容易,更强大。

我被诸如“ Go提供什么都无法在语言X上做的新事物”和“为什么我们需要另一种语言?”之类的评论感到困惑。”在我看来,从某种意义上说,GO不能提供一些以前无法完成的工作,但从另一个意义上讲,新的是,GO将有助于并鼓励使用最好的技术在实践中使用其他语言。

人们要求链接到有关嵌入GO的信息。

这是一个“有效的GO”文件,讨论了嵌入以及提供具体示例的地方。

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

当您已经对GO接口和类型有很好的掌握时,该示例更有意义,但是您可以通过将接口作为一组方法的名称来伪造它,如果您认为结构与C结构相似。

有关结构的更多信息,您可以看到GO语言规范,该规范明确地提到了嵌入式类型的结构的无名成员:

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

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

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

我喜欢。

您使用的语言会影响您的思维模式。 (只需要求C程序员实现“单词计数”即可。他们可能会使用链接列表,然后切换到二进制树以进行性能。但是每个Java/Ruby/Python程序员都会使用字典/哈希。该语言影响了他们的语言大脑如此之多,以至于他们无法想到使用任何其他数据结构。)

借助继承,您必须构建 - 从抽象事物开始,然后将其子类列为细节。您的实际有用代码将埋葬在N类级别中。这使得很难使用对象的“一部分”,因为如果不拖动父类,就无法重复使用代码。

在Go中,您可以以这种方式(使用接口)“建模”课程。但是您不会(不能)以这种方式编码。

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

在旅途中,这不断迫使程序员思考模块化。这会导致耦合较低的程序。低耦合使维护更加容易。 (“哦,看,duck.quack()确实很长而复杂,但至少我知道这不取决于鸭的其余部分。”)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top