State Pattern #

Dalam dunia perangkat lunak, Behavioral Design Pattern fokus pada bagaimana objek berinteraksi dan bertukar informasi. Salah satu pattern yang populer dalam kategori ini adalah State Pattern. Pattern ini membantu kita menangani perilaku objek yang berubah tergantung pada state (keadaan) internalnya.

Apa itu State Pattern? #

State Pattern memungkinkan sebuah objek untuk mengubah perilakunya saat state internalnya berubah. Dari luar, objek tampak seperti mengubah kelasnya sendiri. Dengan kata lain, perilaku objek tergantung pada state yang sedang aktif, dan state tersebut dapat diganti tanpa mengubah kode klien.

Komponen utama State Pattern:

  1. Context: Objek utama yang perilakunya berubah-ubah berdasarkan state.
  2. State Interface / Abstract Class: Mendefinisikan metode yang akan diimplementasikan oleh semua state.
  3. Concrete State: Implementasi nyata dari state tertentu.

Tujuan dari Pattern #

Tujuan utama State Pattern adalah:

  • Menghindari penggunaan if-else / switch statement yang kompleks untuk menangani berbagai kondisi.
  • Memisahkan logika state ke dalam class tersendiri sehingga lebih mudah dikelola.
  • Membuat kode lebih extendable, misalnya menambahkan state baru tanpa mengubah kode lama.

Kapan Cocok Digunakan? #

State Pattern cocok digunakan jika:

  1. Objek memiliki perilaku yang berbeda-beda tergantung state internalnya.
  2. Kondisi if-else atau switch statement menjadi terlalu banyak dan kompleks.
  3. Ingin menambahkan state baru tanpa merusak kode yang sudah ada.

Contoh kasus nyata:

  • Mesin penjual otomatis (Vending Machine) yang memiliki state: NoCoin, HasCoin, SoldOut.
  • Objek koneksi jaringan: Connecting, Connected, Disconnected.
  • Workflow dokumen: Draft, Moderation, Published.

Contoh Implementasi (Golang) #

Berikut contoh sederhana State Pattern pada mesin penjual otomatis:

package main

import "fmt"

// State interface
type State interface {
    InsertCoin()
    EjectCoin()
    TurnCrank()
}

// Context
type VendingMachine struct {
    state State
}

func (v *VendingMachine) SetState(s State) {
    v.state = s
}

func (v *VendingMachine) InsertCoin() {
    v.state.InsertCoin()
}
func (v *VendingMachine) EjectCoin() {
    v.state.EjectCoin()
}
func (v *VendingMachine) TurnCrank() {
    v.state.TurnCrank()
}

// Concrete States

type NoCoinState struct {
    machine *VendingMachine
}
func (s *NoCoinState) InsertCoin() {
    fmt.Println("Coin inserted")
    s.machine.SetState(&HasCoinState{machine: s.machine})
}
func (s *NoCoinState) EjectCoin() {
    fmt.Println("No coin to eject")
}
func (s *NoCoinState) TurnCrank() {
    fmt.Println("Insert coin first")
}

type HasCoinState struct {
    machine *VendingMachine
}
func (s *HasCoinState) InsertCoin() {
    fmt.Println("Coin already inserted")
}
func (s *HasCoinState) EjectCoin() {
    fmt.Println("Coin returned")
    s.machine.SetState(&NoCoinState{machine: s.machine})
}
func (s *HasCoinState) TurnCrank() {
    fmt.Println("Crank turned, dispensing item")
    s.machine.SetState(&NoCoinState{machine: s.machine})
}

func main() {
    machine := &VendingMachine{}
    noCoin := &NoCoinState{machine: machine}
    machine.SetState(noCoin)

    machine.InsertCoin()  // Coin inserted
    machine.TurnCrank()   // Crank turned, dispensing item
    machine.EjectCoin()   // No coin to eject
}

Best Practices #

  1. Pisahkan setiap state dalam class tersendiri untuk menjaga kode bersih dan mudah maintain.
  2. Gunakan Context untuk mengatur state daripada klien yang mengubah state langsung.
  3. Hindari logika bercampur antara state; biarkan setiap state bertanggung jawab penuh atas perilakunya.
  4. Pertimbangkan penggunaan pointer pada context untuk memastikan state dapat dimodifikasi secara dinamis.
  5. Unit test untuk tiap state agar perilaku objek tetap konsisten saat state berubah.

Penutup #

  • State Pattern mengimplementasikan prinsip Open-Closed (Open untuk extension, Closed untuk modification).
  • Mirip dengan Strategy Pattern, tapi fokus State lebih pada perubahan state internal daripada pemilihan algoritma.
  • Berguna dalam aplikasi dengan workflow kompleks atau sistem dengan banyak kondisi berbeda yang dapat berubah secara runtime.
About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact