Я хотів розкрити відповідь, надану тут @jimt . Ця відповідь правильна і дуже допомогла мені розібратися в цьому. Однак існують деякі застереження до обох методів (псевдонім, вбудовування), з якими у мене виникли проблеми.
Примітка : я використовую терміни батько і дитина, хоча я не впевнений, що це найкраще для композиції. В основному, батьківський - це тип, який потрібно змінити локально. Child - це новий тип, який намагається реалізувати цю модифікацію.
Метод 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 (та його підхід до переосмислення об'єктної орієнтації), і тоді у мене виникло саме це питання.