Belajar Interface di Go: Konsep, Contoh, dan Best Practice
Kalau baru kenal Go, biasanya saya cepat ketemu dengan konsep interface.
Awalnya mungkin agak membingungkan: apa bedanya sama struct biasa? Kenapa harus pakai interface?
Singkatnya, interface di Go itu kontrak. Ia bilang: “kalau kamu mau disebut tipe ini, kamu harus punya metode A, B, C.”
Yang keren, di Go saya nggak perlu deklarasi eksplisit kalau sebuah struct implements interface—cukup dengan punya metode yang sesuai, struct itu otomatis memenuhi interface.
Apa Itu Interface?
Interface di Go pada dasarnya cuma daftar metode. Contoh simpel:
type Speaker interface {
Speak() string
}
type Person struct {
Name string
}
func (p Person) Speak() string {
return "Hello, my name is " + p.Name
}
Karena Person
punya metode Speak
, otomatis dia dianggap sebagai Speaker
. Jadi saya bisa pakai:
var s Speaker
s = Person{Name: "Alice"}
fmt.Println(s.Speak()) // Hello, my name is Alice
Kenapa Repot-Repot Pakai Interface?
Beberapa alasan kenapa interface itu berguna banget:
- Loose coupling → kode jadi nggak bergantung ke implementasi spesifik.
- Fleksibel → gampang ganti-ganti implementasi tanpa ubah banyak hal.
- Testing lebih gampang → bisa bikin mock dari interface buat unit test.
Contoh Kasus: Notifikasi
Bayangkan kita mau bikin sistem notifikasi, bisa via email atau SMS.
type Notifier interface {
Notify() string
}
type Email struct{ Address string }
func (e Email) Notify() string { return "Sending email to " + e.Address }
type SMS struct{ Number string }
func (s SMS) Notify() string { return "Sending SMS to " + s.Number }
Sekarang, kita bisa pakai Notifier
tanpa peduli implementasinya:
var n Notifier
n = Email{Address: "example@example.com"}
fmt.Println(n.Notify())
n = SMS{Number: "123-456-7890"}
fmt.Println(n.Notify())
Tanpa Interface vs Dengan Interface
Bayangin kita punya fungsi buat kirim notifikasi. Kalau tanpa interface, kodenya bisa jadi kayak gini:
func SendEmail(e Email) {
fmt.Println(e.Notify())
}
func SendSMS(s SMS) {
fmt.Println(s.Notify())
}
func main() {
SendEmail(Email{Address: "example@example.com"})
SendSMS(SMS{Number: "123-456-7890"})
}
Masalahnya: tiap tipe baru (misalnya WhatsApp, Push Notification), kita harus bikin fungsi lagi. Ribet.
Sekarang coba pakai interface:
func SendNotification(n Notifier) {
fmt.Println(n.Notify())
}
func main() {
SendNotification(Email{Address: "example@example.com"})
SendNotification(SMS{Number: "123-456-7890"})
}
Lebih rapi, fleksibel, dan gampang ditambahin implementasi baru.
Cukup bikin struct dengan Notify()
, otomatis bisa dipakai di SendNotification
.
Interface Bisa Dikombinasi
Go juga ngizinin interface dikomposisi jadi yang lebih kompleks:
type Reader interface {
Read() string
}
type Writer interface {
Write() string
}
type ReadWriter interface {
Reader
Writer
}
Kalau ada struct File
yang punya metode Read
dan Write
, otomatis dia memenuhi ReadWriter
.
Interface Kosong
Satu hal yang sering bikin bingung: interface{}
alias interface kosong.
Artinya? Bisa menampung nilai tipe apa aja.
func printValue(v interface{}) {
fmt.Println(v)
}
func main() {
printValue(42)
printValue("Hello")
printValue(3.14)
}
Gunanya oke buat data generik, tapi jangan kebablasan pakai, soalnya hilang type safety.
Best Practice
- Bikin interface kecil dan fokus → misalnya cuma 1–2 metode.
- Tulis kode berdasarkan interface, bukan implementasi konkrit.
- Jangan kebanyakan pakai
interface{}
kalau nggak benar-benar perlu.
Penutup
Interface adalah salah satu fitur paling penting di Go. Begitu paham konsep dasarnya—bahwa dia cuma kontrak metode—hidup jadi lebih mudah. Entah mau bikin kode fleksibel, lebih gampang dites, atau lebih modular, interface akan selalu jadi senjata andalan.