Command Pattern #

Dalam dunia software engineering, design pattern menjadi pedoman penting untuk menyelesaikan masalah yang sering muncul dalam pengembangan perangkat lunak. Salah satu kategori design pattern adalah Behavioral Pattern, yang fokus pada interaksi antar objek dan cara mereka berkomunikasi.

Salah satu pattern populer dalam kategori ini adalah Command Pattern. Command Pattern sangat berguna ketika kita ingin memisahkan objek yang meminta aksi (invoker) dari objek yang mengeksekusi aksi (receiver), sehingga menciptakan fleksibilitas dan extensibility pada sistem.

Apa itu Command Pattern? #

Command Pattern adalah pattern yang mengubah permintaan (request) menjadi objek yang berdiri sendiri yang berisi semua informasi tentang aksi yang akan dilakukan. Objek ini disebut Command. Dengan pendekatan ini, kita bisa menyimpan, memqueue, menunda, atau log aksi, serta mendukung undo/redo.

Struktur Command Pattern #

  • Command: Interface atau abstraksi yang mendefinisikan method Execute().
  • ConcreteCommand: Implementasi nyata dari Command yang memanggil aksi di receiver.
  • Receiver: Objek yang benar-benar melakukan aksi.
  • Invoker: Objek yang meminta eksekusi command.
  • Client: Objek yang membuat command dan mengaitkannya dengan receiver.

Tujuan dari Pattern #

  • Decoupling: Memisahkan pemanggil aksi dari pelaksana aksi.
  • Undo/Redo: Memudahkan implementasi undo/redo dengan menyimpan history command.
  • Queue & Logging: Memungkinkan menyimpan command untuk dieksekusi nanti atau untuk logging.
  • Macro Commands: Menggabungkan beberapa command menjadi satu aksi besar.

Kapan Cocok Digunakan? #

Command Pattern cocok digunakan ketika:

  • Kita membutuhkan request/aksi yang bisa disimpan, dibatalkan, atau diulang.
  • Ingin decoupling antara pemanggil aksi dan pelaksana aksi.
  • Sistem membutuhkan queueing, scheduling, atau logging perintah.
  • Ada kebutuhan untuk menyusun operasi kompleks dari operasi sederhana.

Contoh Implementasi (Golang) #

package main

import "fmt"

// Command interface
type Command interface {
    Execute()
}

// Receiver
 type Light struct {}
 func (l *Light) On()  { fmt.Println("Light is ON") }
 func (l *Light) Off() { fmt.Println("Light is OFF") }

// Concrete Commands
 type LightOnCommand struct {
    light *Light
 }
 func (c *LightOnCommand) Execute() { c.light.On() }

 type LightOffCommand struct {
    light *Light
 }
 func (c *LightOffCommand) Execute() { c.light.Off() }

// Invoker
 type RemoteControl struct {
    command Command
 }
 func (r *RemoteControl) SetCommand(cmd Command) { r.command = cmd }
 func (r *RemoteControl) PressButton() { r.command.Execute() }

func main() {
    light := &Light{}

    lightOn := &LightOnCommand{light: light}
    lightOff := &LightOffCommand{light: light}

    remote := &RemoteControl{}

    remote.SetCommand(lightOn)
    remote.PressButton()

    remote.SetCommand(lightOff)
    remote.PressButton()
}

Output:

Light is ON
Light is OFF

Best Practices #

  1. Keep Commands Simple: Setiap Command sebaiknya hanya melakukan satu aksi yang jelas.
  2. Use Command History for Undo/Redo: Simpan command yang dieksekusi agar bisa di-undo atau di-redo.
  3. Favor Composition over Inheritance: Gunakan objek command yang dikomposisikan dengan receiver daripada mewarisi banyak class.
  4. Macro Commands: Gabungkan beberapa command untuk aksi kompleks jika diperlukan.
  5. Logging & Queueing: Command dapat dicatat atau ditempatkan dalam queue untuk dieksekusi di kemudian hari.

Penutup #

Command Pattern sering digunakan dalam GUI applications, task scheduling, job queues, dan sistem yang membutuhkan undo/redo functionality. Di Golang, pattern ini menjadi lebih elegan karena interface sederhana dan kemampuan function pointers dapat membuat command menjadi lebih fleksibel.

Dengan penerapan Command Pattern, sistem menjadi lebih modular, mudah diperluas, dan fleksibel untuk perubahan permintaan di masa depan.

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact