/ 7 min read
Membuat Tipe Data Sendiri: Struct, Method, dan Interface - Becoming Gopher
Selamat datang kembali di petualangan Becoming Gopher! Di bab sebelumnya, kita sudah menjadi arsitek program dengan merakit logika kita ke dalam function
yang rapi dan bisa digunakan kembali.
Sejauh ini, kita hanya bekerja dengan tipe data dasar seperti string
, int
, dan bool
. Tapi, dunia nyata jauh lebih kompleks. Bagaimana cara kita merepresentasikan ‘Pengguna’, ‘Produk’, atau ‘Buku’ yang memiliki banyak atribut (nama, harga, stok, dll.) dalam satu kesatuan?
Di sinilah kita naik level dari sekadar ‘programmer’ menjadi ‘desainer sistem’. Hari ini kita akan belajar tiga konsep yang saling terkait untuk menciptakan tipe data kita sendiri:
Struct
: Kita akan belajar membuat ‘cetakan’ atau ‘blueprint’ data kita sendiri.Method
: Lalu, kita akan memberikan ‘kemampuan’ atau ‘perilaku’ pada data kita dengan menempelkan fungsi langsung padanya.Interface
: Terakhir, kita akan belajar mendefinisikan ‘kontrak’ atau ‘aturan main’ yang bisa diikuti oleh tipe data apa pun.
Ini adalah fondasi dari Object-Oriented Programming (OOP) ala Go. Siap untuk mulai merancang duniamu sendiri di dalam kode? Ayo kita mulai!
Struct
: Cetakan untuk Datamu
Struct (struktur) adalah sebuah template data yang digunakan untuk menggabungkan beberapa tipe data berbeda menjadi satu kesatuan yang logis. Anggap saja struct
adalah cara kita membuat tipe data baru yang lebih kompleks.
Misalnya, kita ingin merepresentasikan sebuah buku. Sebuah buku memiliki judul (string
), penulis (string
), tahun terbit (int
), dan status ketersediaan (bool
). Dengan struct
, kita bisa membungkus semua informasi itu.
Membuat dan Menggunakan Struct
Pertama, kita definisikan ‘cetakan’-nya menggunakan type
dan struct
.
// Mendefinisikan blueprint untuk sebuah Bukutype Book struct { Title string Author string Year int Available bool}
Sekarang kita punya tipe data baru bernama Book
. Mari kita buat sebuah variabel dengan tipe tersebut.
func main() { // Membuat data buku berdasarkan blueprint 'Book' buku1 := Book{ Title: "Go untuk Pemula", Author: "Khoirul Anwar", Year: 2025, Available: true, }
// Mengakses data di dalamnya menggunakan notasi titik (.) fmt.Println("Judul:", buku1.Title) fmt.Println("Penulis:", buku1.Author)
// Kita juga bisa mengubah nilainya buku1.Available = false fmt.Println("Status sekarang:", buku1.Available)}
Praktik Terbaik: Fungsi Konstruktor
Meskipun kita bisa membuat struct
secara langsung seperti di atas, praktik yang umum di Go adalah menggunakan fungsi konstruktor. Ini adalah sebuah fungsi konvensional yang bertujuan untuk membuat instance dari struct
dengan cara yang terpusat dan valid.
// Fungsi yang 'merakit' dan mengembalikan sebuah Book barufunc NewBook(title string, author string, year int) *Book { return &Book{ Title: title, Author: author, Year: year, Available: true, // Bisa atur nilai default di sini }}
func main() { // Penggunaan konstruktor bukuBaru := NewBook("Go untuk Pemula", "Khoirul Anwar", 2025) fmt.Println("Judul dari konstruktor:", bukuBaru.Title)}
Pola ini sangat berguna untuk menjaga konsistensi saat membuat objek yang kompleks.
Sekilas Info: Struct Tags
Sebelum lanjut, ada satu fitur keren dari struct
yang akan sangat berguna saat kamu membuat aplikasi web, yaitu struct tags. Ini adalah anotasi metadata yang kita berikan pada field untuk memberi instruksi pada package lain, misalnya saat mengubah struct
menjadi JSON.
import "encoding/json"
type Product struct { Name string `json:"name"` // Saat diubah ke JSON, field ini akan bernama "name" Price int `json:"price"` IsReady bool `json:"is_ready,omitempty"` // "omitempty" berarti field ini akan diabaikan jika nilainya kosong SecretKey string `json:"-"` // Tanda "-" berarti field ini akan selalu diabaikan}
Tidak perlu pusing sekarang, cukup tahu bahwa fitur ini ada dan sangat powerful.
Method
: Memberi Kemampuan pada Struct
Data tanpa perilaku itu pasif. Kita bisa memberi ‘kemampuan’ atau ‘perilaku’ pada struct
kita dengan menempelkan fungsi langsung padanya. Fungsi yang ‘terikat’ pada sebuah struct
inilah yang disebut method.
Perbedaannya sederhana:
- Fungsi biasa:
lakukanSesuatu(data)
- Method:
data.lakukanSesuatu()
Mari kita berikan method
pada struct Book
kita untuk menampilkan informasinya sendiri.
type Book struct { Title string Author string Available bool}
// Ini adalah METHOD karena terikat pada (b Book)// 'b' disebut sebagai receiverfunc (b Book) PrintInfo() { fmt.Println("--- Informasi Buku ---") fmt.Println("Judul:", b.Title) fmt.Println("Penulis:", b.Author) if b.Available { fmt.Println("Status: Tersedia") } else { fmt.Println("Status: Tidak tersedia") }}
func main() { book := Book{Title: "Go untuk Pemula", Author: "Khoirul Anwar", Available: true}
// Memanggil method langsung dari variabel struct book.PrintInfo()}
Dengan method
, struct
kita kini tidak hanya menyimpan data, tapi juga tahu bagaimana cara memproses datanya sendiri.
Interface
: Kontrak Perilaku Universal
Konsep ini sedikit abstrak, tapi sangat kuat. Bayangkan sebuah stopkontak di dinding. Stopkontak itu tidak peduli apa yang akan Anda colok: charger HP, adaptor laptop, atau kipas angin. Ia hanya punya satu aturan atau kontrak: “Jika kamu punya dua pin logam yang sesuai, kamu bisa terhubung denganku.”
Interface di Go adalah persis seperti itu: sebuah kontrak perilaku. Ia hanya mendefinisikan sekumpulan method yang harus dimiliki, tanpa peduli siapa yang memilikinya.
// Siapapun yang punya method PrintInfo(), dia adalah seorang 'Printable'type Printable interface { PrintInfo()}
Implementasi Interface yang Ajaib
Inilah keunikan Go. Sebuah struct
tidak perlu secara eksplisit bilang, “Hei, saya mengimplementasikan interface Printable!”. Cukup dengan memiliki semua method yang disyaratkan oleh interface tersebut, maka Go secara otomatis menganggapnya patuh pada kontrak. Ini disebut implicit interface implementation.
Mari kita lihat contohnya. Kita punya Book
dan Magazine
.
// Interface (Kontrak)type Printable interface { PrintInfo()}
// Struct pertamatype Book struct { Title string Author string}
// Book punya method PrintInfo(), jadi ia adalah Printablefunc (b Book) PrintInfo() { fmt.Println("Buku:", b.Title, "oleh", b.Author)}
// Struct keduatype Magazine struct { Title string Issue int}
// Magazine juga punya PrintInfo(), jadi ia juga Printablefunc (m Magazine) PrintInfo() { fmt.Println("Majalah:", m.Title, "Edisi ke", m.Issue)}
// Fungsi ini menerima TIPE APAPUN yang memenuhi kontrak Printablefunc displayItem(p Printable) { p.PrintInfo()}
func main() { buku := Book{Title: "Go untuk Pemula", Author: "Khoirul Anwar"} majalah := Magazine{Title: "Tech Today", Issue: 42}
displayItem(buku) // Bisa, karena buku adalah Printable displayItem(majalah) // Bisa juga, karena majalah juga Printable}
Fungsi displayItem
tidak perlu tahu detail tentang Book
atau Magazine
. Ia hanya peduli satu hal: “Apakah benda ini bisa di-PrintInfo()
?”. Fleksibilitas inilah yang membuat interface menjadi pilar utama dalam membangun software yang modular di Go.
interface{}
- Si Super Fleksibel
Ada satu interface spesial: interface{}
. Karena ia tidak mensyaratkan method apa pun (kontraknya kosong), maka semua tipe data di Go secara otomatis memenuhinya. interface{}
bisa menampung nilai apa saja, menjadikannya tipe data paling fleksibel di Go.
func printAnything(data interface{}) { fmt.Println("Data yang diterima:", data)}
printAnything(123)printAnything("hello")printAnything(Book{Title: "Go", Author: "X"})
Petualangan Hari Ini Selesai!
Luar biasa! Kita baru saja menyelesaikan salah satu bab paling konseptual dalam perjalanan “Becoming Gopher”. Kita tidak lagi hanya menggunakan tipe data bawaan, tapi sudah bisa menciptakan tipe data kita sendiri yang merepresentasikan dunia nyata.
Singkatnya, hari ini kita sudah belajar:
- Membungkus kumpulan data terkait ke dalam sebuah ‘cetakan’ bernama
struct
. - Memberikan ‘kemampuan’ atau perilaku pada
struct
denganmethod
. - Mendefinisikan ‘kontrak’ perilaku universal yang membuat kode kita fleksibel dengan
interface
.
Kita sudah bisa merancang data dan perilakunya. Tapi, ada satu topik fundamental yang sengaja kita simpan sampai sekarang karena butuh pemahaman yang matang: pointer
. Di episode selanjutnya, kita akan membongkar misteri pointer dan melihat bagaimana ia bisa membuat program kita jauh lebih efisien.
Coba pikirkan, objek apa dari dunia nyata yang bisa kamu modelkan dengan struct
dan interface
? Bagikan idemu di kolom komentar!