Lompati ke konten utama
Modul 5 · Pelajaran 3 dari 523/50 dalam kursus~12 min
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

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

Bidang 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:

Go
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:

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

Jika 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):

Go
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:

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

Penyematan 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

Latihan#go.m5.l3.e1
Upaya: 0Memuat…

Tentukan Anjing yang menyematkan Hewan (penyematan, tanpa nama bidang) dan memiliki bidang string Breed.

Memuat editor…
Tunjukkan petunjuk

Penyematan: tulis TYPE tanpa memberinya nama field.

Solusi tersedia setelah 3 upaya

Latihan#go.m5.l3.e2
Upaya: 0Memuat…

Tambahkan metode string Greet() ke Hewan dan panggil langsung pada variabel Anjing (promosi metode).

Memuat editor…
Tunjukkan petunjuk

Metode yang ditentukan pada Hewan dipromosikan menjadi Anjing berkat penyematan.

Solusi tersedia setelah 3 upaya

Quiz#go.m5.l3.e3
Siap

Apakah Go mendukung warisan klasik?

Go
type B struct { /* extends A? */ }
Pilihan jawaban

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.