Встраивание вместо наследования в Go - PullRequest
52 голосов
/ 13 ноября 2009

Что вы думаете об этом дизайнерском решении? Какие у него преимущества и какие недостатки?

Ссылки:

Ответы [ 7 ]

36 голосов
/ 13 ноября 2009

Банда из важнейшего принципа 4 - «предпочесть композицию наследованию»; Go заставляет следовать за ним; -).

29 голосов
/ 19 ноября 2009

В комментарии вы задались вопросом, достаточно ли идеи встраивания, чтобы «полностью заменить наследование». Я бы сказал, что ответ на этот вопрос «да». Несколько лет назад я очень коротко поиграл с системой Tcl OO под названием Snit , которая использовала состав и делегирование, исключая наследование. Снит все еще сильно отличается от подхода Го, но в этом отношении у них есть некоторое общее философское обоснование. Это механизм объединения частей функциональности и ответственности, а не иерархия для классов.

Как уже говорили другие, на самом деле речь идет о том, какие методы программирования хотят поддерживать разработчики языка. У всех таких выборов есть свои плюсы и минусы; Я не думаю, что «лучшие практики» - это фраза, которая обязательно применяется здесь. Вероятно, мы увидим, что кто-то в конце концов разработает слой наследования для Go.

(Для всех читателей, знакомых с Tcl, я чувствовал, что Snit немного ближе к «ощущению» языка, чем [incr Tcl]. Tcl полностью посвящен делегированию, по крайней мере, моему образу мышления)

12 голосов
/ 13 ноября 2009

Единственное реальное использование для наследования:

  • Полиморфизм

    • Система статического набора текста в интерфейсе 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;
    }
}

Здесь наследование и полиморфизм объединяются, и вы не можете перевести это на 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
}

Согласно примечанию, это совершенно неправильный способ написания кода, так как каждый делает Java в Go. Если бы кто-то написал такую ​​вещь на Go, она, вероятно, была бы организована совсем иначе, чем эта.

7 голосов
/ 07 марта 2013

Встраивание обеспечивает автоматическое делегирование. Этого само по себе недостаточно, чтобы заменить наследование, поскольку встраивание не дает никакой формы полиморфизма. Интерфейсы Go действительно обеспечивают полиморфизм, они немного отличаются от интерфейсов, которые вы можете использовать (некоторые люди сравнивают их с типизацией Duck или структурной типизацией).

В других языках иерархии наследования должны быть тщательно спроектированы, потому что изменения широко распространены и, следовательно, их трудно осуществить. Go избегает этих ловушек, предоставляя мощную альтернативу.

Вот статья, которая немного углубляется в ООП с помощью Go: http://nathany.com/good

3 голосов
/ 19 января 2014

Мне нравится.

Язык, который вы используете, влияет на ваши шаблоны мышления. (Просто попросите программиста C реализовать «подсчет слов». Возможно, они будут использовать связанный список, а затем переключатся на двоичное дерево для повышения производительности. Но каждый программист на Java / Ruby / Python будет использовать Dictionary / Hash. Язык повлиял на их мозги так много, что они не могут думать об использовании какой-либо другой структуры данных.)

С наследованием вы должны строить вниз - начните с абстрактной вещи, затем подклассифицируйте ее к специфике. Ваш действительно полезный код будет погружен в класс N уровней. Это затрудняет использование «части» объекта, потому что вы не можете повторно использовать код без перетаскивания в родительские классы.

В Go вы можете «моделировать» свои классы таким образом (с помощью интерфейсов). Но вы не можете (не можете) кодировать таким образом.

Вместо этого вы можете использовать встраивание. Ваш код может быть разбит на маленькие, изолированные модули, каждый со своими данными. Это делает повторное использование тривиальным. Эта модульность имеет мало общего с вашими "большими" объектами. (т. е. в Go вы можете написать метод quack (), который даже не знает о вашем классе Duck. Но на типичном языке ООП вы не можете объявить «моя реализация Duck.quack () не имеет зависимостей от любые другие методы утки. ")

В Go это постоянно заставляет программиста задумываться о модульности. Это приводит к программам, которые имеют низкую связь. Низкое сцепление значительно облегчает техническое обслуживание. («О, смотри, Duck.quack () действительно длинный и сложный, но, по крайней мере, я знаю, что это не зависит от остальной части Duck.»)

3 голосов
/ 22 декабря 2012

Люди запросили ссылки на информацию о встраивании в Go.

Вот документ «Effective 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

3 голосов
/ 18 ноября 2009

Я только сейчас узнаю о Go, но так как вы спрашиваете мнение, я предложу его на основе того, что я знаю до сих пор. Встраивание, по-видимому, типично для многих других вещей в Go, то есть явной языковой поддержки лучших практик, которые уже применяются на существующих языках. Например, как заметил Алекс Мартелли, банда из четырех человек говорит «предпочитаю композицию наследованию». Go не только удаляет наследование, но и делает композицию проще и мощнее, чем в C ++ / Java / C #.

Я был озадачен комментариями типа "Go не предоставляет ничего нового, чего я не могу уже сделать на языке X" и "зачем нам нужен другой язык?" Мне кажется, что в одном смысле Go не предоставляет ничего нового, чего нельзя было бы сделать раньше с помощью какой-либо работы, но в другом смысле новшество заключается в том, что Go будет способствовать использованию лучших методов уже на практике использую другие языки.

...