Я хотел расширить ответ, данный @jimt здесь . Этот ответ верен и очень помог мне разобраться в этом. Однако есть некоторые предостережения в отношении обоих методов (псевдоним, встраивание), с которыми у меня возникли проблемы.
примечание : я использую термины родительский и дочерний, хотя я не уверен, что это лучший вариант для композиции. По сути, parent - это тип, который вы хотите изменить локально. Дочерний - это новый тип, который пытается реализовать эту модификацию.
Метод 1 - определение типа
type child parent
// or
type MyThing imported.Thing
- Предоставляет доступ к полям.
- Не предоставляет доступа к методам.
type child struct {
parent
}
// or with import and pointer
type MyThing struct {
*imported.Thing
}
- Предоставляет доступ к полям.
- Предоставляет доступ к методам.
- Требует рассмотрения при инициализации.
Резюме
- При использовании метода композиции встроенный родительский элемент не будет инициализирован, если это указатель. Родитель должен быть инициализирован отдельно.
- Если встроенный родительский элемент является указателем и не инициализируется при инициализации дочернего элемента, произойдет ошибка разыменования нулевого указателя.
- И определение типа, и варианты внедрения предоставляют доступ к полям родительского элемента.
- Определение типа не разрешает доступ к родительским методам, но встраивание родительского делает.
Вы можете увидеть это в следующем коде.
рабочий пример на детской площадке
package main
import (
"fmt"
)
type parent struct {
attr string
}
type childAlias parent
type childObjParent struct {
parent
}
type childPointerParent struct {
*parent
}
func (p *parent) parentDo(s string) { fmt.Println(s) }
func (c *childAlias) childAliasDo(s string) { fmt.Println(s) }
func (c *childObjParent) childObjParentDo(s string) { fmt.Println(s) }
func (c *childPointerParent) childPointerParentDo(s string) { fmt.Println(s) }
func main() {
p := &parent{"pAttr"}
c1 := &childAlias{"cAliasAttr"}
c2 := &childObjParent{}
// When the parent is a pointer it must be initialized.
// Otherwise, we get a nil pointer error when trying to set the attr.
c3 := &childPointerParent{}
c4 := &childPointerParent{&parent{}}
c2.attr = "cObjParentAttr"
// c3.attr = "cPointerParentAttr" // NOGO nil pointer dereference
c4.attr = "cPointerParentAttr"
// CAN do because we inherit parent's fields
fmt.Println(p.attr)
fmt.Println(c1.attr)
fmt.Println(c2.attr)
fmt.Println(c4.attr)
p.parentDo("called parentDo on parent")
c1.childAliasDo("called childAliasDo on ChildAlias")
c2.childObjParentDo("called childObjParentDo on ChildObjParent")
c3.childPointerParentDo("called childPointerParentDo on ChildPointerParent")
c4.childPointerParentDo("called childPointerParentDo on ChildPointerParent")
// CANNOT do because we don't inherit parent's methods
// c1.parentDo("called parentDo on childAlias") // NOGO c1.parentDo undefined
// CAN do because we inherit the parent's methods
c2.parentDo("called parentDo on childObjParent")
c3.parentDo("called parentDo on childPointerParent")
c4.parentDo("called parentDo on childPointerParent")
}
“extension methods are not object-oriented”
) для C #, но, глядя на них сегодня, я сразу вспомнил интерфейсы Go (и его подход к переосмыслению объектной ориентации), и тогда у меня возник именно этот вопрос.