cv.cm

Pemrograman · Pola desain

Penjelasan pola Strategy beserta contohnya

4 menit baca

Jawaban singkat

Strategy adalah pola desain behavioral yang mendefinisikan sekumpulan algoritma, menaruh masing-masing di objek terpisah dengan interface yang sama, dan membuatnya dapat ditukar. Kode yang memakai algoritma, yang disebut context, menyimpan referensi ke sebuah strategy dan memanggilnya tanpa tahu yang mana. Di TypeScript, strategy sering kali hanya berupa fungsi, seperti comparator yang diberikan ke array.sort().

Tujuan dan masalah yang diselesaikan

Tujuan: mendefinisikan sekumpulan algoritma, membungkus masing-masing, dan membuatnya dapat ditukar. Strategy memungkinkan algoritma berubah secara independen dari klien yang memakainya. Ini salah satu pola behavioral dalam buku Gang of Four (GoF), yang juga menyebutnya Policy.

Misalkan checkout menghitung ongkos kirim. Pengiriman standar berupa biaya tetap, ekspres lebih mahal per kilogram, dan ambil di toko gratis. Versi pertama menaruh ketiga rumus dalam satu method dengan rantai if/else pada jenis pengiriman. Setiap opsi baru, seperti kurir mitra, berarti mengedit method itu lagi, dan menguji satu rumus berarti melewati semua yang lain.

Dengan Strategy, setiap rumus menjadi objek atau fungsi sendiri dengan signature yang sama. Checkout menerima strategy yang dipilih dan memanggilnya. Menambah kurir berarti menambah satu strategy baru; checkout tidak berubah.

Struktur dan peserta

PeranTanggung jawab
StrategyInterface bersama untuk semua versi algoritma
Concrete strategySatu implementasi algoritma
ContextMenyimpan sebuah strategy, memanggilnya saat butuh algoritma, dan dapat membiarkan klien menggantinya
ClientMemilih concrete strategy dan memberikannya ke context

Implementasi TypeScript

Karena fungsi adalah nilai di TypeScript, interface strategy yang paling sederhana adalah tipe fungsi. Context-nya adalah class Checkout.

type Parcel = { weightKg: number; subtotal: number };
type ShippingStrategy = (parcel: Parcel) => number;

const standard: ShippingStrategy = (p) => (p.subtotal >= 50 ? 0 : 4.99);
const express: ShippingStrategy = (p) => 9.99 + 1.5 * p.weightKg;
const pickup: ShippingStrategy = () => 0;

class Checkout {
  constructor(private shipping: ShippingStrategy) {}

  setShipping(strategy: ShippingStrategy) {
    this.shipping = strategy;
  }

  total(parcel: Parcel): number {
    return Math.round((parcel.subtotal + this.shipping(parcel)) * 100) / 100;
  }
}

const parcel = { weightKg: 2, subtotal: 30 };
const checkout = new Checkout(standard);
console.log(checkout.total(parcel)); // 34.99
checkout.setShipping(express);
console.log(checkout.total(parcel)); // 42.99
checkout.setShipping(pickup);
console.log(checkout.total(parcel)); // 30

Jika sebuah strategy butuh konfigurasi atau beberapa method, gunakan interface dan class. Versi ini juga memberi setiap strategy nama yang dapat ditampilkan UI:

interface Shipping {
  readonly label: string;
  cost(weightKg: number): number;
}

class Courier implements Shipping {
  readonly label: string;
  constructor(private ratePerKg: number, company: string) {
    this.label = company + " courier";
  }
  cost(weightKg: number) { return this.ratePerKg * weightKg; }
}

const options: Shipping[] = [new Courier(2, "North"), new Courier(3.5, "Fast")];
for (const o of options) console.log(o.label, o.cost(4));
// North courier 8
// Fast courier 14

Sebelum dan sesudah

Sebelumnya, pilihan algoritma tertanam di dalam method:

function shippingCost(type: string, p: Parcel): number {
  if (type === "standard") return p.subtotal >= 50 ? 0 : 4.99;
  else if (type === "express") return 9.99 + 1.5 * p.weightKg;
  else if (type === "pickup") return 0;
  throw new Error("Unknown shipping type " + type);
}
// After: a lookup table of strategies replaces the chain
const strategies: Record<string, ShippingStrategy> = { standard, express, pickup };

Potongan ini memakai ulang tipe dan fungsi dari contoh pertama. Tabelnya menjadikan kumpulan opsi sebagai data, bukan kode, sehingga bisa dibangun dari konfigurasi atau diperluas oleh plugin.

Kapan dipakai dan kapan tidak

  • Pakai bila ada beberapa cara mengerjakan tugas yang sama dan pilihannya bergantung pada konfigurasi, input pengguna, atau data.
  • Pakai untuk menghilangkan blok kondisional besar yang memilih di antara algoritma.
  • Pakai untuk menguji setiap algoritma secara terpisah dan agar kode lain dapat menambah algoritma baru tanpa mengedit context.
  • Jangan dipakai bila hanya ada satu algoritma dan tidak ada kebutuhan nyata untuk mengubahnya. Interface tambahan hanya menambah lapisan tanpa manfaat.
  • Jangan membuat class bila parameter fungsi sudah cukup. Di TypeScript, mengoper fungsi adalah bentuk Strategy yang ringan.

Penggunaan di dunia nyata

  • Array.prototype.sort(compareFn) menerima strategy perbandingan sebagai fungsi, sehingga sort yang sama dapat mengurutkan angka, tanggal, atau nama.
  • Interface Comparator di Java adalah strategy untuk Collections.sort dan List.sort.
  • Passport.js, middleware autentikasi untuk Node.js, menyebut setiap metode login (password lokal, penyedia OAuth) sebagai strategy dan memungkinkan aplikasi memasangnya.
  • Library kompresi dan hashing memungkinkan pemanggil memilih algoritma lewat interface bersama.

Pola terkait

PolaPerbedaannya dengan Strategy
StateStruktur sama, tetapi state berganti sendiri saat objek berubah; strategy biasanya dipilih klien dan tetap sampai diganti.
Template MethodTemplate Method memvariasikan bagian algoritma lewat pewarisan dan menetapkan langkah keseluruhan; Strategy mengganti seluruh algoritma lewat komposisi.
CommandCommand adalah permintaan untuk dijalankan nanti; strategy adalah cara mengerjakan tugas yang dipanggil context saat dibutuhkan.
DecoratorDecorator mengubah perilaku objek dengan membungkusnya dari luar; strategy mengubah perilaku dari dalam dengan mengganti satu bagian.

FAQ

Apa perbedaan antara Strategy dan State?

Strukturnya hampir identik. Strategy dipilih klien untuk menentukan cara tugas dikerjakan, sedangkan objek state mengubah state context saat ini sendiri sebagai akibat tindakannya.

Apakah mengoper callback itu pola Strategy?

Ya, di bahasa dengan fungsi kelas satu, callback yang mengimplementasikan bagian variabel dari algoritma, seperti comparator, adalah pola Strategy dalam bentuk paling sederhana.

Apa perbedaan antara Strategy dan Template Method?

Template Method memakai pewarisan: base class menetapkan langkah dan subclass mengisi sebagian darinya. Strategy memakai komposisi: context menyimpan objek atau fungsi yang bisa diganti saat runtime.

Apakah Strategy mengikuti prinsip open/closed?

Ya. Anda dapat menambah strategy baru tanpa mengubah context, sehingga context terbuka untuk perluasan tetapi tertutup untuk modifikasi.

Semua tutorial pemrograman