Pelajaran modul (3/5)
Komposisi (penyematan)
Pergi tidak memiliki warisan. Jawaban idiomatis untuk menggunakan kembali adalah komposisi, dan khususnya penyematan: termasuk satu jenis di dalam yang lain tanpa memberinya nama bidang. Bidang dan metode tipe yang tersemat dipromosikan, menjadi dapat diakses langsung seolah-olah mereka termasuk tipe luar.
Penyematan struktur
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 fieldBidang yang disematkan masih dapat diakses berdasarkan nama jenisnya:
c.Animal.Name berfungsi dan identik dengan c.Name. Hal ini diperlukan ketika
ada tabrakan.
Override (parsial): metode "membayangi"
Jika tipe luar mendefinisikan metode dengan nama yang sama dengan yang dipromosikan satu, metodenya "menang". Dari dalam Anda selalu dapat mencapai yang tertanam satu melalui nama tipe:
func (c Dog) Greet() string {
return "BAU! " + c.Animal.Greet()
}Ini bukan pewarisan polimorfik: ini hanyalah resolusi nama leksikal. Untuk polimorfisme Anda menggunakan antarmuka (Modul 6).
Penyematan ganda
Anda dapat menyematkan beberapa jenis:
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 TimerJika dua tipe tertanam memiliki metode dengan nama SAMA, akses langsung
menjadi ambigu dan kompiler memerlukan bentuk eksplisit
(s.Logger.Foo() atau s.Timer.Foo()).
Penyematan antarmuka
Antarmuka juga dapat "disematkan" (lihat Modul 6):
type ReadWriter interface {
io.Reader
io.Writer
}Ini setara dengan gabungan metode Reader dan Writer.
Kapan menggunakan komposisi eksplisit (bukan penyematan)
Jika Anda menginginkan bidang bernama dan tidak ada promosi otomatis, gunakan biasa komposisi:
type Dog struct {
Anim Animal // NORMAL field, no promotion
Breed string
}
c.Anim.Name // you need the field namePenyematan adalah alat delegasi yang berguna, bukan pilihan desain benar-benar lebih baik daripada komposisi eksplisit: gunakan saat Anda benar-benar menginginkannya untuk mempromosikan API tipe dalam.
Cobalah
Tentukan Anjing yang menyematkan Hewan (penyematan, tanpa nama bidang) dan memiliki bidang string Breed.
Tunjukkan petunjuk
Penyematan: tulis TYPE tanpa memberinya nama field.
Solusi tersedia setelah 3 upaya
Tambahkan metode string Greet() ke Hewan dan panggil langsung pada variabel Anjing (promosi metode).
Tunjukkan petunjuk
Metode yang ditentukan pada Hewan dipromosikan menjadi Anjing berkat penyematan.
Solusi tersedia setelah 3 upaya
Apakah Go mendukung warisan klasik?
type B struct { /* extends A? */ }Rekap
- Tanpa warisan: Anda menulis dengan penyematan.
- Embedding = menulis TYPE tanpa nama field pada struct.
- Bidang dan metode tipe tersemat dapat dipromosikan.
- Akses eksplisit selalu tersedia:
outer.Inner.Field. - Penggantian "leksikal": metode tipe luar membayangi metode yang dipromosikan.
- Menyematkan ≠ is-a: untuk polimorfisme gunakan interface.