Aturan Pemenuhan Interface
Termasuk bagian Pemrograman Berorientasi Objek dalam perjalanan GO di Coddy. Pelajaran 43 dari 107.
Meskipun Go menggunakan duck typing untuk pemenuhan interface secara implisit, ada aturan khusus yang menentukan apakah suatu tipe benar-benar memenuhi interface. Memahami aturan ini membantumu menghindari bug yang sulit diketahui.
Aturan terpenting berkaitan dengan pointer receiver. Jika sebuah method didefinisikan dengan pointer receiver, hanya pointer ke tipe tersebut yang memenuhi interface, bukan nilainya sendiri:
type Saver interface {
Save() string
}
type Document struct{ Name string }
func (d *Document) Save() string { // receiver pointer
return "Saved: " + d.Name
}
func Process(s Saver) {
fmt.Println(s.Save())
}
Karena Save() memiliki receiver pointer, hanya *Document yang memenuhi Saver:
func main() {
doc := Document{Name: "report.txt"}
Process(&doc) // berfungsi, pointer memenuhi interface
// Process(doc) // error saat compile, nilai tidak memenuhi interface
}
Namun, kebalikannya lebih fleksibel. Jika sebuah method memiliki penerima nilai, tipe nilai dan pointer keduanya memenuhi interface. Go secara otomatis melakukan dereferensi pointer saat memanggil method dengan penerima nilai:
func (d Document) Info() string { // penerima nilai
return d.Name
}
// Document dan *Document sama-sama memenuhi interface yang mensyaratkan Info()
Asimetri ini terjadi karena Go selalu bisa mendapatkan nilai dari pointer (dengan melakukan dereferensi), tetapi Go tidak selalu bisa mendapatkan pointer dari suatu nilai (nilai tersebut mungkin tidak dapat diambil alamatnya). Dengan mengingat aturan ini, kamu bisa mencegah error compiler yang membingungkan saat bekerja dengan interface.
Tantangan
MudahMari kita buat sistem konfigurasi yang menunjukkan pengaruh receiver pointer dan receiver nilai terhadap pemenuhan interface. Kamu akan membuat tipe yang pilihan receivernya menentukan apakah nilai, pointer, atau keduanya dapat digunakan dengan sebuah interface.
Kamu akan menyusun kodemu dalam tiga file:
config.go: Definisikan interfaceConfigurableyang memerlukan dua method:GetValue() stringdanSetValue(string). Lalu buat dua tipe konfigurasi:ReadOnlyConfigdengan fieldValue: gunakan receiver nilai untukGetValue()(mengembalikan Value) dan receiver pointer untukSetValue()(memperbarui Value)Settingdengan fieldData: gunakan receiver nilai untuk kedua method (GetValue mengembalikan Data, SetValue hanya mencetak "Cannot modify" tanpa mengubah apa pun)
processor.go: Buat fungsi bernamaProcessConfigyang menerimaConfigurabledan string nilai baru. Fungsi ini harus mencetak nilai saat ini menggunakanGetValue(), memanggilSetValue()dengan nilai baru, lalu mencetak nilai itu lagi untuk menunjukkan perubahan yang terjadi.main.go: Baca detail konfigurasi dari input dan tunjukkan aturan pemenuhan interface:- Buat
ReadOnlyConfigdan berikan pointer keProcessConfig(diperlukan karenaSetValuememiliki receiver pointer) - Buat
Settingdan berikan nilainya secara langsung keProcessConfig(berfungsi karena kedua method memiliki receiver nilai)
- Buat
Input berikut akan diberikan:
- Baris 1: Nilai awal untuk ReadOnlyConfig
- Baris 2: Nilai baru untuk ReadOnlyConfig
- Baris 3: Nilai awal untuk Setting
- Baris 4: Nilai baru yang akan dicoba untuk Setting
Fungsi ProcessConfig harus mencetak dalam format ini:
Current: [value]
Current: [value after SetValue]Contohnya, jika diberikan debug, production, localhost, dan remote, outputmu seharusnya:
Current: debug
Current: production
Current: localhost
Cannot modify
Current: localhostPerhatikan bahwa ReadOnlyConfig benar-benar mengubah nilainya (karena kita memberikan pointer), sedangkan Setting tidak berubah (method SetValue-nya dengan receiver nilai tidak dapat mengubah nilai aslinya). Inti pentingnya adalah nilai ReadOnlyConfig saja tidak memenuhi Configurable, hanya pointer yang memenuhinya, sedangkan nilai Setting dapat digunakan secara langsung karena semua method-nya menggunakan receiver nilai.
Coba sendiri
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Baca input
scanner.Scan()
rocInitial := scanner.Text()
scanner.Scan()
rocNew := scanner.Text()
scanner.Scan()
settingInitial := scanner.Text()
scanner.Scan()
settingNew := scanner.Text()
// TODO: Buat ReadOnlyConfig dengan nilai rocInitial
// TODO: Teruskan POINTER ke ProcessConfig (diperlukan karena SetValue memiliki pointer receiver)
// TODO: Buat Setting dengan nilai settingInitial
// TODO: Teruskan VALUE langsung ke ProcessConfig (bisa karena kedua method memiliki value receiver)
// Gunakan variabel-variabel ini agar tidak muncul error variabel yang tidak digunakan
_ = rocInitial
_ = rocNew
_ = settingInitial
_ = settingNew
}
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Pemrograman Berorientasi Objek
1Dasar-Dasar OOP di Go
File EksternalWorkspace Go dan modulePaket dan ImporNama yang Diekspor dan Tidak DieksporPengantar OOP di Gostruct sebagai classMendefinisikan method pada structPointer receiver vs value receiverInisialisasi structFungsi KonstruktorUlasan: Kalkulator Sederhana4Interface
Pengenalan InterfaceImplementasi ImplisitInterface sebagai KontrakInterface Kosong (any)Type AssertionType SwitchKomposisi InterfaceInterface Stringer & ErrorUlasan: Kalkulator Bentuk7Enkapsulasi
Field yang Diekspor vs Tidak DieksporEnkapsulasi Tingkat PackageMethod Getter dan SetterPenyembunyian Informasi di GoRingkasan: Data Siswa10Generics (Go 1.18+)
Pengantar GenericsParameter TipeBatasan TipeStruct GenerikCara Alternatif untuk Method GenerikRingkasan Koleksi Generik2Mendalami Tipe dan Struct
Tipe Dasar dan KompositDefinisi Tipe KustomTag StructStruct AnonimStruct BersarangNilai Nol dan Nilai BawaanRangkuman: Buku Kontak5Komposisi daripada Pewarisan
Mengapa Go Tidak Memiliki PewarisanDasar Penyematan StructPromosi methodMenyematkan Beberapa StructPenyematan vs AgregasiMembayangi method yang DisematkanRangkuman: Hierarki Karyawan8Penanganan Error & OOP
Interface errorTipe Error KustomPembungkusan Error (fmt.Errorf)Error Sentinelerrors.Is() dan errors.As()Panic, Defer, dan RecoverRangkuman: Parser File3Pointer dan Memori
Dasar-Dasar Pointer di GoPointer ke StructPass by Value vs ReferenceFungsi new()Garbage Collection di GoRekap: Pembuat Linked List6Polimorfisme di Go
Polimorfisme melalui InterfaceDuck Typing di GoAturan Pemenuhan InterfaceKoleksi PolimorfikInjeksi DependensiRangkuman: Pemroses Pembayaran9Konkruensi & OOP
Dasar-dasar GoroutineChannel & KomunikasiChannel Ber-buffer vs Tanpa BufferPernyataan selectsync.Mutex & sync.RWMutexsync.WaitGroupDesain Struct yang Aman untuk ThreadRingkasan: Worker PoolLatihan sendiri: Compiler Go online