Przejdź do głównej treści
eLearner.app
Moduł 5 · Lekcja 3 z 523/50 w kursie~12 min
Lekcje modułu (3/5)

Kompozycja (osadzanie)

Idź nie ma dziedzictwa. Idiomatyczna odpowiedź na pytanie: ponowne użycie brzmi: skład, a w szczególności osadzanie: obejmuje jeden rodzaj wewnątrz innego, nie nadając mu nazwy pola. Dziedziny i metody typ osadzony zostaje promowany i staje się bezpośrednio dostępny, jak gdyby należeli do typu zewnętrznego.

Osadzanie struktur

Go
type Animal struct {
    Name string
}

func (a Animal) Greet() string {
    return "ciao da " + a.Name
}

type Dog struct {
    Animal        // embedding — NO field name
    Breed string
}

c := Dog{
    Animal: Animal{Name: "Rex"},
    Breed:  "Pastore",
}

fmt.Println(c.Name)         // "Rex" — promoted from Animal
fmt.Println(c.Greet())      // "ciao da Rex" — promoted method
fmt.Println(c.Breed)        // own field

Osadzone pole jest nadal dostępne po nazwie typu: c.Animal.Name działa i jest identyczny z c.Name. Jest to potrzebne kiedy dochodzi do kolizji.

Zastąpienie (częściowe): metoda „cieniowanie”

Jeśli typ zewnętrzny definiuje metodę o tej samej nazwie, co promowana po pierwsze, jego metoda „wygrywa”. Od wewnątrz zawsze możesz dotrzeć do osadzonego jeden poprzez nazwę typu:

Go
func (c Dog) Greet() string {
    return "BAU! " + c.Animal.Greet()
}

Nie jest to dziedziczenie polimorficzne: jest to po prostu rozpoznawanie nazw leksykalnych. Do polimorfizmu używasz interfejsów (Moduł 6).

Wiele osadzań

Możesz osadzić wiele typów:

Go
type Logger struct{ /*...*/ }
func (l Logger) Log(msg string) { /*...*/ }

type Timer struct{ /*...*/ }
func (t Timer) Now() time.Time { /*...*/ }

type Service struct {
    Logger
    Timer
    Name string
}

s := Service{Name: "api"}
s.Log("start")   // promoted from Logger
s.Now()          // promoted from Timer

Jeśli dwa typy osadzone mają metodę o TEJ SAMEJ nazwie, dostęp bezpośredni staje się niejednoznaczne i kompilator wymaga jawnej formy (KODEF0 lub KODEF1).

Osadzanie interfejsu

Interfejsy mogą być również „wbudowane” (patrz Moduł 6):

Go
type ReadWriter interface {
    io.Reader
    io.Writer
}

Jest to równoważne połączeniu metod Reader i Writer.

Kiedy używać jawnej kompozycji (nie osadzania)

Jeśli chcesz mieć nazwane pole i brak automatycznej promocji, użyj zwykłego skład:

Go
type Dog struct {
    Anim  Animal    // NORMAL field, no promotion
    Breed string
}
c.Anim.Name   // you need the field name

Osadzanie to przydatne narzędzie delegowania, a nie wybór projektu ściśle lepsza niż jawna kompozycja: używaj jej, kiedy naprawdę chcesz w celu promowania interfejsu API typu wewnętrznego.

Spróbuj

Ćwiczenie#go.m5.l3.e1
Próby: 0Ładowanie...

Zdefiniuj psa, który osadza zwierzę (osadzanie, bez nazwy pola) i ma pole tekstowe Rasa.

Ładowanie edytora...
Pokaż wskazówkę

Osadzanie: wpisz TYP bez nadawania mu nazwy pola.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie#go.m5.l3.e2
Próby: 0Ładowanie...

Dodaj metodę łańcuchową Greet() do Animal i wywołaj ją bezpośrednio na zmiennej Dog (promocja metody).

Ładowanie edytora...
Pokaż wskazówkę

Metoda zdefiniowana na Animal jest promowana do Dog dzięki osadzeniu.

Rozwiązanie dostępne po 3 próbach

Quiz#go.m5.l3.e3
Gotowe

Czy Go obsługuje dziedziczenie klasyczne?

Go
type B struct { /* extends A? */ }
Opcje odpowiedzi

Podsumowanie

  • Brak dziedziczenia: komponujesz z osadzaniem.
  • Osadzanie = zapisywanie TYPE bez nazwy pola w strukturze.
  • Pola i metody typu osadzonego są promowane.
  • Dostęp jawny jest zawsze dostępny: outer.Inner.Field.
  • Nadpisanie „leksykalne”: metoda typu zewnętrznego przesłania metodę promowaną.
  • Osadzanie ≠ is-a: dla polimorfizmu użyj interfejsów.