Salt la conținutul principal
Modulul 5 · Lecția 3 din 523/50 în curs~12 min
Lecții din modul (3/5)

Compoziție (încorporare)

Du-te nu are moștenire. Răspunsul idiomatic la reutilizare este compoziție și, în special, încorporare: inclusiv un tip în interiorul altuia fără a-i da un nume de câmp. Domeniile și metodele de tipul încorporat este promovat, devenind direct accesibil ca și cum au aparținut tipului exterior.

Încorporarea structurii

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

Câmpul încorporat este încă accesibil prin numele tipului: c.Animal.Name funcționează și este identic cu c.Name. Este nevoie când sunt ciocniri.

Suprascriere (parțial): metoda „umbrire”

Dacă tipul exterior definește o metodă cu același nume ca și cea promovată unul, metoda ei „câștigă”. Din interior puteți ajunge oricând la încorporat unul prin numele tipului:

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

Nu este o moștenire polimorfă: este doar rezoluție lexicală de nume. Pentru polimorfism folosiți interfețe (Modulul 6).

Încorporare multiplă

Puteți încorpora mai multe tipuri:

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

Dacă două tipuri încorporate au o metodă cu ACEȘI nume, acces direct devine ambiguu și compilatorul necesită forma explicită (s.Logger.Foo() sau s.Timer.Foo()).

Încorporarea interfeței

Interfețele pot fi, de asemenea, „încorporate” (vezi Modulul 6):

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

Este echivalent cu unirea metodelor lui Reader și Writer.

Când să folosiți compoziția explicită (nu încorporarea)

Dacă doriți un câmp numit și nicio promovare automată, utilizați simplu compozitie:

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

Încorporarea este un instrument de delegare la îndemână, nu o alegere de design strict mai bună decât compoziția explicită: folosește-o când vrei cu adevărat pentru a promova API-ul tipului interior.

Încearcă

Exercițiu#go.m5.l3.e1
Încercări: 0Se încarcă…

Definiți Câine care încorporează Animal (încorporare, fără nume de câmp) și are un câmp șir de rasă.

Se încarcă editorul...
Afișează indiciu

Încorporare: scrieți TYPE fără a-i da un nume de câmp.

Soluție disponibilă după 3 încercări

Exercițiu#go.m5.l3.e2
Încercări: 0Se încarcă…

Adăugați o metodă șir Greet() la Animal și apelați-o direct pe o variabilă Dog (promovarea metodei).

Se încarcă editorul...
Afișează indiciu

Metoda definită pe Animal este promovată la Câine datorită încorporarii.

Soluție disponibilă după 3 încercări

Quiz#go.m5.l3.e3
Gata

Go suportă moștenirea clasică?

Go
type B struct { /* extends A? */ }
Opțiuni de răspuns

Recapitulare

  • Fără moștenire: compuneți cu embedding.
  • Embedding = scrierea TYPE-ului fără un nume de câmp în structura.
  • Câmpurile și metodele de tip încorporat sunt promovate.
  • Accesul explicit este întotdeauna disponibil: outer.Inner.Field.
  • Suprascriere "lexicală": metoda tipului exterior o umbră pe cea promovată.
  • Încorporarea ≠ is-a: pentru polimorfism folosiți interfețe.