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
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 fieldCâ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:
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:
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 TimerDacă 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):
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:
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ă
Definiți Câine care încorporează Animal (încorporare, fără nume de câmp) și are un câmp șir de rasă.
Afișează indiciu
Încorporare: scrieți TYPE fără a-i da un nume de câmp.
Soluție disponibilă după 3 încercări
Adăugați o metodă șir Greet() la Animal și apelați-o direct pe o variabilă Dog (promovarea metodei).
Afișează indiciu
Metoda definită pe Animal este promovată la Câine datorită încorporarii.
Soluție disponibilă după 3 încercări
Go suportă moștenirea clasică?
type B struct { /* extends A? */ }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.