Pemrograman · Pola desain
Penjelasan pola Command beserta contohnya
Jawaban singkat
Command adalah pola desain behavioral yang mengubah permintaan menjadi objek mandiri yang berisi semua yang dibutuhkan untuk menjalankannya. Karena permintaan itu sebuah objek, Anda bisa menyimpannya, mengoperkannya, memasukkannya ke antrean, mencatatnya, dan meng-undo-nya nanti. Objek yang memicu command (misalnya tombol) tidak perlu tahu objek mana yang mengerjakan atau bagaimana caranya.
Tujuan dan masalah yang diselesaikan
Tujuan: membungkus permintaan sebagai objek, sehingga klien dapat diparameterisasi dengan permintaan berbeda, permintaan dapat diantrekan atau dicatat, dan operasi yang bisa di-undo didukung. Command adalah salah satu pola behavioral dalam buku Gang of Four (GoF).
Pikirkan editor teks. Aksi yang sama, seperti "tebalkan teks", bisa dipicu oleh tombol toolbar, item menu, dan pintasan keyboard Ctrl+B. Jika masing-masing memanggil editor secara langsung, logikanya terduplikasi di tiga tempat. Lebih buruk lagi, editor juga harus mendukung undo: setelah pengguna menekan Ctrl+Z, program perlu tahu aksi terakhir apa dan bagaimana membalikkannya.
Command menyelesaikan kedua masalah itu. Setiap aksi menjadi objek dengan method execute, dan opsional method undo. Tombol, menu, dan pintasan semuanya memegang objek command yang sama. Editor menyimpan daftar riwayat command yang sudah dijalankan, sehingga undo berarti mengambil command terakhir dari daftar dan memanggil method undo-nya.
Struktur dan peserta
| Peran | Tanggung jawab |
|---|---|
| Command | Interface dengan method execute (dan sering undo) |
| Concrete command | Menyimpan receiver dan parameter, lalu memanggil method receiver di dalam execute |
| Receiver | Objek yang tahu cara mengerjakan pekerjaan sebenarnya, seperti dokumen atau rekening bank |
| Invoker | Meminta sebuah command berjalan; bisa menyimpan riwayat untuk undo dan redo |
| Client | Membuat concrete command dan menghubungkannya dengan receiver dan invoker |
Implementasi TypeScript: undo dan redo
TextDocument adalah receiver. InsertText dan DeleteLast adalah command yang mengingat cukup informasi untuk membalikkan dirinya sendiri. Editor adalah invoker dengan dua stack: satu untuk undo dan satu untuk redo.
interface Command {
execute(): void;
undo(): void;
}
class TextDocument {
text = "";
}
class InsertText implements Command {
constructor(private doc: TextDocument, private value: string) {}
execute() { this.doc.text += this.value; }
undo() { this.doc.text = this.doc.text.slice(0, -this.value.length); }
}
class DeleteLast implements Command {
private removed = "";
constructor(private doc: TextDocument, private count: number) {}
execute() {
this.removed = this.doc.text.slice(-this.count);
this.doc.text = this.doc.text.slice(0, -this.count);
}
undo() { this.doc.text += this.removed; }
}
class Editor {
private done: Command[] = [];
private undone: Command[] = [];
run(cmd: Command) { cmd.execute(); this.done.push(cmd); this.undone = []; }
undo() { const c = this.done.pop(); if (c) { c.undo(); this.undone.push(c); } }
redo() { const c = this.undone.pop(); if (c) { c.execute(); this.done.push(c); } }
}
const doc = new TextDocument();
const editor = new Editor();
editor.run(new InsertText(doc, "Hello"));
editor.run(new InsertText(doc, ", world"));
editor.run(new DeleteLast(doc, 7));
console.log(doc.text); // Hello
editor.undo();
console.log(doc.text); // Hello, world
editor.redo();
console.log(doc.text); // HelloSebelum dan sesudah
Tanpa pola ini, UI memanggil dokumen secara langsung dan undo tidak mungkin dilakukan, karena tidak ada yang mencatat apa yang terjadi:
// Before: each trigger calls the receiver directly
boldButton.onclick = () => doc.makeBold(selection);
menuBold.onclick = () => doc.makeBold(selection);
// After: every trigger shares one command, and the editor records it
const bold = () => new MakeBold(doc, selection);
boldButton.onclick = () => editor.run(bold());
menuBold.onclick = () => editor.run(bold());Versi kedua adalah potongan kode yang mengasumsikan class command MakeBold seperti yang di atas. UI tidak lagi tahu cara kerja penebalan, dan setiap aksi melewati satu tempat di mana ia bisa dicatat, di-log, atau di-undo.
Kapan dipakai dan kapan tidak
- Pakai untuk undo dan redo, makro (daftar command yang dijalankan bersama), dan riwayat command.
- Pakai untuk mengantrekan atau menjadwalkan pekerjaan: job queue menyimpan objek command dan worker menjalankannya nanti.
- Pakai untuk mencatat atau memutar ulang operasi, misalnya mengirim command yang sudah diserialisasi ke server lain.
- Pakai untuk memisahkan elemen UI dari kode yang dipicunya.
- Jangan dipakai bila Anda hanya butuh callback. Di TypeScript fungsi sudah merupakan nilai kelas satu, jadi
button.onclick = saveadalah command yang sangat memadai tanpa undo. Gunakan class command saat Anda butuh perilaku tambahan sepertiundo, nama untuk ditampilkan, atau serialisasi.
Penggunaan di dunia nyata
- Interface
Runnabledi Java adalah command dengan satu method,run, yang dipakai thread dan executor untuk menjalankan pekerjaan nanti. - Action Redux adalah objek biasa yang menjelaskan sebuah perubahan; reducer menerapkannya, dan log action memungkinkan time-travel debugging. Ini kerabat dekat Command, dengan logika disimpan di reducer, bukan di command.
- VS Code dan banyak editor lain mendaftarkan command bernama yang bisa dikaitkan ke tombol, menu, dan command palette.
- Migrasi database sering punya langkah
updandown, yaitu pasangan execute dan undo dari sebuah command.
Pola terkait
| Pola | Perbedaannya dengan Command |
|---|---|
| Strategy | Strategy menukar cara satu tugas dikerjakan; Command mengemas apa yang harus dikerjakan (sebuah permintaan) agar bisa disimpan, diantrekan, atau di-undo. |
| Memento | Memento menyimpan snapshot state untuk undo; Command menyimpan operasi dan membalikkannya. Keduanya sering digabung ketika pembalikan sulit dilakukan. |
| Chain of Responsibility | Sebuah command bisa dioper sepanjang rantai handler untuk menemukan yang menjalankannya. |
| Composite | Makro command adalah Composite: command yang tersusun dari command lain. |
| Observer | Observer memberi tahu banyak listener tentang sebuah event; Command adalah satu permintaan yang dikirim ke satu receiver. |
FAQ
Bagaimana pola Command mengimplementasikan undo?
Setiap command punya method undo yang membalikkan execute-nya sendiri, sering memakai data yang disimpan saat eksekusi. Invoker menyimpan stack command yang sudah dijalankan dan memanggil undo pada yang terbaru.
Apa perbedaan antara Command dan Strategy?
Strategy mewakili berbagai cara mengerjakan tugas yang sama dan dipilih oleh objek yang memakainya. Command mewakili permintaan tertentu, lengkap dengan receiver dan parameternya, yang dapat dijalankan nanti, diantrekan, atau di-undo.
Apakah fungsi callback itu command?
Callback adalah command minimal: ia mengemas aksi yang berjalan nanti. Anda butuh objek command yang lengkap bila juga menginginkan undo, deskripsi, serialisasi, atau metadata lain.
Apa itu macro command?
Macro command menyimpan daftar command dan menjalankannya berurutan. Undo-nya membalikkan command tersebut dalam urutan terbalik, yang merupakan penerapan pola Composite.