От меньшего - экспоненциально больше
Если в C ++ и Java речь идет об иерархиях типов и таксономии типов, то в Go речь идет о композиции.
От меньшего - экспоненциально больше
Если в C ++ и Java речь идет об иерархиях типов и таксономии типов, то в Go речь идет о композиции.
Ответы:
Он имеет в виду, что где бы вы использовали что-то порядка:
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;
};
Этот вопрос / проблема отчасти похож на этот .
В 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, даже если она вызывает вам думать, а не просто переосмысливать концепции модели вашего приложения.