Chapter 6 of 10
Setelah menyelesaikan bab ini, Anda mampu:
struct;struct adalah tipe bentukan yang menyatukan beberapa field. Gunakan nama field berawalan huruf besar jika field perlu diakses dari package lain.
package main
import "fmt"
type Task struct {
ID int
Title string
Done bool
}
func main() {
t := Task{ID: 1, Title: "Belajar struct"}
t.Done = true
fmt.Printf("%d: %s, selesai=%t\n", t.ID, t.Title, t.Done)
}
Utamakan literal bernama seperti Task{ID: 1, Title: "..."}. Bentuk ini tetap jelas ketika urutan field berubah.
Method adalah fungsi yang terikat pada tipe. Receiver nilai menerima salinan; receiver pointer dapat mengubah nilai asal.
package main
import (
"errors"
"fmt"
"strings"
)
type Task struct {
ID int
Title string
Done bool
}
func NewTask(id int, title string) (Task, error) {
title = strings.TrimSpace(title)
if id < 1 {
return Task{}, errors.New("ID harus positif")
}
if title == "" {
return Task{}, errors.New("judul tidak boleh kosong")
}
return Task{ID: id, Title: title}, nil
}
func (t *Task) Complete() {
t.Done = true
}
func (t Task) String() string {
status := "[ ]"
if t.Done {
status = "[x]"
}
return fmt.Sprintf("%s %d %s", status, t.ID, t.Title)
}
func main() {
t, err := NewTask(1, "Belajar method")
if err != nil {
panic(err)
}
t.Complete()
fmt.Println(t)
}
Gunakan receiver pointer ketika method memodifikasi objek atau struct cukup besar. Gunakan receiver nilai untuk operasi baca pada nilai kecil. Jangan mencampur keduanya tanpa alasan yang jelas.
Implementasi interface di Go bersifat implisit. Sebuah tipe memenuhi interface jika memiliki seluruh method yang diminta.
package main
import "fmt"
type Describer interface {
Describe() string
}
type Task struct{ Title string }
func (t Task) Describe() string { return "Tugas: " + t.Title }
type Note struct{ Text string }
func (n Note) Describe() string { return "Catatan: " + n.Text }
func PrintDescription(d Describer) {
fmt.Println(d.Describe())
}
func main() {
PrintDescription(Task{Title: "Belajar interface"})
PrintDescription(Note{Text: "Interface sebaiknya kecil"})
}
Definisikan interface di sisi pemakai, bukan otomatis di sisi implementasi. Terima interface jika fungsi benar-benar membutuhkan variasi perilaku; kembalikan tipe konkret agar API tetap mudah digunakan.
Jika method didefinisikan pada *Task, biasanya hanya *Task yang memenuhi interface terkait. Method dengan receiver nilai tersedia bagi Task dan *Task.
var _ fmt.Stringer = Task{}
Pernyataan di atas merupakan pemeriksaan saat kompilasi bahwa Task memenuhi fmt.Stringer.
Go tidak memiliki pewarisan kelas. Bangun tipe kompleks dengan menyusun tipe yang lebih kecil.
Embedding mempromosikan field dan method, tetapi nilai embedded tetap merupakan bagian objek, bukan hubungan “adalah”.
package main
import "fmt"
type Metadata struct {
CreatedBy string
}
type Task struct {
Metadata
Title string
}
func main() {
t := Task{
Metadata: Metadata{CreatedBy: "Ayu"},
Title: "Belajar komposisi",
}
fmt.Println(t.CreatedBy, t.Title)
}
Gunakan field bernama ketika embedding membuat API ambigu. Komposisi eksplisit sering lebih mudah dibaca:
type Service struct {
store TaskStore
}
package main
import (
"errors"
"fmt"
"strings"
)
type Task struct {
ID int
Title string
}
type TaskStore interface {
Save(Task) error
All() []Task
}
type MemoryStore struct {
tasks []Task
}
func (s *MemoryStore) Save(t Task) error {
s.tasks = append(s.tasks, t)
return nil
}
func (s *MemoryStore) All() []Task {
return append([]Task(nil), s.tasks...)
}
type Service struct {
store TaskStore
}
func NewService(store TaskStore) Service {
return Service{store: store}
}
func (s Service) Add(id int, title string) error {
title = strings.TrimSpace(title)
if id < 1 || title == "" {
return errors.New("ID dan judul tidak valid")
}
return s.store.Save(Task{ID: id, Title: title})
}
func main() {
store := &MemoryStore{}
service := NewService(store)
if err := service.Add(1, "Susun model domain"); err != nil {
panic(err)
}
fmt.Println(store.All())
}
All mengembalikan salinan slice agar pemanggil tidak dapat mengubah penyimpanan secara tidak sengaja.
nil meskipun nilai konkret di dalamnya berupa pointer nil.Priority int pada Task dan method Validate() error dengan rentang 1–5.TaskReader yang hanya memiliki All() []Task.FindByID(int) (Task, bool) pada MemoryStore.Logger kecil dengan method Printf(string, ...any) dan injeksikan ke Service.Metadata.struct memodelkan data; method menempatkan perilaku dekat dengan data.