Когда Роб Пайк говорит «Go - это композиция», что именно он имеет в виду? [закрыто]

12

От меньшего - экспоненциально больше

Если в C ++ и Java речь идет об иерархиях типов и таксономии типов, то в Go речь идет о композиции.

CMinus
источник

Ответы:

13

Он имеет в виду, что где бы вы использовали что-то порядка:

class A : public B {};

в чем-то вроде Java или C ++, в Go вы бы использовали (что-то эквивалентное):

class A {
    B b;
};

Да, это обеспечивает наследственные возможности. Давайте немного расширим пример выше:

struct B {
    int foo() {}
};

struct A { 
    B b;
};

A a;

a.foo();  // not allowed in C++ or Java, but allowed in Go.

Однако для этого вы используете синтаксис, который не разрешен в C ++ или Java - вы оставляете внедренный объект без собственного имени, так что это больше похоже на:

struct A {
   B;
};
Джерри Гроб
источник
1
Мне любопытно, я делаю это на C ++ (предпочитаю композицию). Предоставляет ли go функции, которые помогают мне сочинять, когда в Java / C ++ мне придется наследовать?
Дуг Т.
2
@DougT .: Да, я отредактировал пример, показывающий общее представление о (части) того, что он позволяет.
Джерри Гроб
2
Я думаю, что это упускает из виду: разница не просто синтаксическая, подразумевающая, что вы используете вложение для построения своей таксономии. Дело в том, что отсутствие переопределения метода ООП мешает вам создать классическую таксономию, и вы должны вместо этого использовать композицию.
Денис Сегюре
1
@dystroy: По сравнению с Java, у вас, вероятно, есть смысл. По сравнению с C ++, не так много, потому что (по крайней мере, среди тех, кто имеет представление), эти гигантские таксономии в последний раз были замечены около 20 лет назад.
Джерри Гроб
1
@dystroy: Вы все еще не понимаете. Трехуровневая иерархия в современном C ++ рядом с неслыханной. В C ++ вы увидите их в библиотеке iostreams и в иерархии исключений - но близко к нигде. Если бы библиотека iostreams создавалась сегодня, я думаю, можно с уверенностью сказать, что и этого не будет. Итог: ваши аргументы показывают меньше о C ++, чем о том, как вы вне связи с ним. Учитывая, что вы не использовали его в течение десятилетий, это имеет смысл. Что не имеет смысла, так это пытаться сказать, как C ++ используется на основе этого устаревшего опыта.
Джерри Гроб
8

Этот вопрос / проблема отчасти похож на этот .

В Go у вас нет ООП.

Если вы хотите «специализировать» объект, вы делаете это путем встраивания, которое является композицией, но с некоторыми вкусностями, делающими его частично похожим на наследование. Вы делаете это так:

type ConnexionMysql struct {
    *sql.DB
}

В этом примере ConnexionMysql является разновидностью * sql.DB, и вы можете вызвать ConnexionMysql функции, определенные в * sql.DB:

type BaseMysql struct {
    user     string
    password string
    database string
}

func (store *BaseMysql) DB() (ConnexionMysql, error) {
    db, err := sql.Open("mymysql", store.database+"/"+store.user+"/"+store.password)
    return ConnexionMysql{db}, err
}

func (con ConnexionMysql) EtatBraldun(idBraldun uint) (*EtatBraldun, error) {
    row := con.QueryRow("select pv, pvmax, pa, tour, dla, faim from compte where id=?", idBraldun)
    // stuff
    return nil, err
}

// somewhere else:
con, err := ms.bd.DB()
defer con.Close()
// ...
somethings, err = con.EtatBraldun(id)

Так что на первый взгляд вы можете подумать, что эта композиция - инструмент для создания вашей обычной таксономии.

Но

если функция, определенная в * sql.DB, вызывает другие функции, определенные в * sql.DB, она не будет вызывать функции, переопределенные в ConnexionMysql, даже если они существуют.

С классическим наследованием вы часто делаете что-то вроде этого:

func (db *sql.DB) doComplexThing() {
   db.doSimpleThing()
   db.doAnotherSimpleThing()
}

func (db *sql.DB) doSimpleThing() {
   // standard implementation, that we expect to override
}

То есть вы определяете doComplexThingсуперкласс как организацию по вызовам специализаций.

Но в Go это вызвало бы не специализированную функцию, а функцию «суперкласса».

Итак, если вы хотите иметь алгоритм, требующий вызова некоторых функций, определенных в * sql.DB, но переопределенных в ConnexionMySQL (или других специализациях), вы не можете определить этот алгоритм как функцию * sql.DB, но должны определить его в другом месте. и эта функция будет только составлять вызовы к предоставленной специализации.

Вы можете сделать это, используя интерфейсы:

type interface SimpleThingDoer {
   doSimpleThing()
   doAnotherSimpleThing()
}

func doComplexThing(db SimpleThingDoer) {
   db.doSimpleThing()
   db.doAnotherSimpleThing()
}

func (db *sql.DB) doSimpleThing() {
   // standard implementation, that we expect to override
}

func (db ConnexionMySQL) doSimpleThing() {
   // other implemenation
}

Это сильно отличается от классического переопределения иерархии классов.

В частности, вы, очевидно, не можете напрямую иметь третий уровень, наследующий реализацию функции от второго.

На практике вы прекратите использовать в основном (ортогональные) интерфейсы и позволите функции составлять вызовы для предоставленной реализации вместо того, чтобы иметь "суперкласс" реализации, организующий эти вызовы.

По моему опыту, это приводит к практическому отсутствию иерархий глубже одного уровня.

Слишком часто в других языках у вас возникает рефлекс, когда вы видите, что концепция A является специализацией концепции B, чтобы утвердить этот факт путем создания класса B и класса A в качестве подкласса B. Вместо создания вашего Программируя ваши данные, вы тратите время на воспроизведение таксономии объектов в вашем коде, исходя из того, что это реальность.

В Go вы не можете определить общий алгоритм и специализировать его. Вы должны определить общий алгоритм и убедиться, что он общий и работает с предоставленными реализациями интерфейса.

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

Денис Сегюре
источник