Go tidak punya kata kunci constructor seperti __construct pada PHP atau method khusus __init__ pada Python. Zero value struct yang kita bahas sebelumnya memang sering cukup, tetapi domain tertentu menuntut aturan: akun tanpa nama atau saldo negatif tidak boleh tercipta. Untuk kebutuhan itu, komunitas Go memakai constructor pattern — fungsi biasa bernama NewX yang membangun nilai struct sekaligus memvalidasi masukannya. Pada chapter ini kita bangun constructor NewAkun lengkap dengan validasi dan penanganan error-nya.
Pola NewX #
Constructor Go hanyalah fungsi dengan konvensi nama: diawali New diikuti nama tipe. Bentuk yang paling umum mengembalikan pasangan (Akun, error) — constructor mengembalikan nilai zero value Akun{} bersama nil saat berhasil, atau Akun{} bersama error saat validasi gagal. Karena berupa fungsi biasa, constructor juga bisa menerima parameter apa pun, memanggil fungsi lain, dan diberi documentation comment seperti fungsi lainnya.
Validasi diletakkan di baris-baris awal sebelum pembentukan nilai. Strategi ini disebut fail fast: kesalahan terdeteksi di pintu masuk, bukan menyebar sebagai struct cacat ke seluruh program. Pemanggil wajib memeriksa error sebelum memakai struct hasilnya — pola pengecekan if err != nil yang diperdalam pada section Error Handling
.
Program Utuh #
Berkas constructor-demo.go memuat struct Akun dengan field privat beserta constructor-nya:
package main
import (
"errors"
"fmt"
"strings"
)
type Akun struct {
nama string
saldo int
aktif bool
}
func NewAkun(nama string, saldoAwal int) (Akun, error) {
if strings.TrimSpace(nama) == "" {
return Akun{}, errors.New("nama tidak boleh kosong")
}
if saldoAwal < 0 {
return Akun{}, fmt.Errorf("saldo awal %d tidak boleh negatif", saldoAwal)
}
return Akun{nama: nama, saldo: saldoAwal, aktif: true}, nil
}
func (a Akun) Ringkasan() string {
return fmt.Sprintf("Akun %q (aktif: %v) saldo %d", a.nama, a.aktif, a.saldo)
}
func main() {
a, err := NewAkun("Budi", 500000)
if err != nil {
fmt.Println("galat:", err)
return
}
fmt.Println(a.Ringkasan())
_, err = NewAkun(" ", 100)
fmt.Println("galat:", err)
_, err = NewAkun("Siti", -1)
fmt.Println("galat:", err)
}Jalankan program dari direktori chapter:
go run constructor-demo.goOutput yang Dihasilkan:
Akun "Budi" (aktif: true) saldo 500000
galat: nama tidak boleh kosong
galat: saldo awal -1 tidak boleh negatifMari kita telaah baris demi baris program di atas:
- Baris 9–13 (
type Akun struct): mendeklarasikan struct dengan field kecil semuanya (nama,saldo,aktif) — huruf kecil membuat field tidak bisa diisi dari package lain. - Baris 15–23 (
func NewAkun): constructor dengan tanda tangan(Akun, error); seluruh validasi selesai sebelum baris pengembalian nilai sukses. - Baris 16–18 (validasi nama): menolak nama kosong dengan
errors.New— cukup untuk pesan statis; pembuatan error dibahas pada chapter Membuat Error . - Baris 19–21 (validasi saldo): menolak saldo negatif dengan
fmt.Errorfkarena pesannya memuat nilaisaldoAwallewat verb%d. - Baris 22 (nilai sukses): membangun
Akundengan field privat terisi penuh dan mengembalikannilsebagai error — statusaktifotomatistruehanya lewat jalur ini. - Baris 30–35 (pemanggilan sukses): memanggil
NewAkununtukBudi, memeriksaerrlebih dulu, lalu mencetak ringkasan akun. - Baris 37–41 (pemanggilan gagal): memanggil constructor dengan masukan salah; identifier kosong
_membuang struct yang tidak terpakai dan error-nya dicetak sebagai bukti validasi bekerja.
Field Privat sebagai Penjaga Aturan #
Field kecil membuat struct hanya bisa dibangun lewat jalur yang kita sediakan — dari package lain, Akun{nama: "Budi"} tidak dikompilasi karena nama tidak terlihat. Constructor berada di package yang sama dengan struct sehingga berhak mengisi field privat. Contoh kita memakai satu berkas package main, jadi aturan visibilitas belum terasa; begitu struct dipindah ke package terpisah, constructor menjadi satu-satunya pintu masuk yang sah. Pola Ringkasan di baris 25–27 memperkuat hal yang sama: pembacaan field tetap tersedia lewat method, penulisan hanya lewat constructor.
Alternatif: Zero Value yang Siap Pakai #
Constructor bukan satu-satunya cara memulai struct. Standard library kerap merancang struct yang zero value-nya langsung berguna lalu membiarkan pengguna mengonfigurasi field publik yang perlu:
srv := &http.Server{Addr: ":8080"}http.Server tanpa konfigurasi tetap sah dengan nilai bawaan; field Addr diisi lewat struct literal karena memang dimaksudkan publik. Pilih pendekatan sesuai kebutuhan: zero value saat nilai bawaan sudah benar, constructor saat aturan validasi harus ditegakkan. Bentuk keduanya dirangkum tabel berikut:
| Bentuk | Kapan dipakai |
|---|---|
NewT() T | tanpa kemungkinan gagal |
NewT() (T, error) | ada validasi atau kegagalan eksternal |
NewT() *T | hasil dibagikan sebagai satu pointer |
| zero value + field publik | nilai bawaan sudah benar |
Referensi #
- Dokumentasi package errors
—
errors.Newuntuk error sederhana. - Dokumentasi package fmt
—
fmt.Errorfuntuk error berformat. - Tutorial: Handle errors
— idiom pengecekan
err != nil.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
constructor-demo.go
Unduh berkaspackage main
import (
"errors"
"fmt"
"strings"
)
type Akun struct {
nama string
saldo int
aktif bool
}
func NewAkun(nama string, saldoAwal int) (Akun, error) {
if strings.TrimSpace(nama) == "" {
return Akun{}, errors.New("nama tidak boleh kosong")
}
if saldoAwal < 0 {
return Akun{}, fmt.Errorf("saldo awal %d tidak boleh negatif", saldoAwal)
}
return Akun{nama: nama, saldo: saldoAwal, aktif: true}, nil
}
func (a Akun) Ringkasan() string {
return fmt.Sprintf("Akun %q (aktif: %v) saldo %d", a.nama, a.aktif, a.saldo)
}
func main() {
a, err := NewAkun("Budi", 500000)
if err != nil {
fmt.Println("galat:", err)
return
}
fmt.Println(a.Ringkasan())
_, err = NewAkun(" ", 100)
fmt.Println("galat:", err)
_, err = NewAkun("Siti", -1)
fmt.Println("galat:", err)
}