Apa itu Strategy Pattern? Penjelasan Lengkap untuk Pemula
Pendahuluan
Pernahkah kamu menulis kode yang penuh dengan if-else atau switch-case hanya untuk memilih cara melakukan sesuatu? Misalnya, memilih metode pembayaran, memilih algoritma sorting, atau memilih cara menghitung diskon? Kalau pernah, Strategy Pattern hadir sebagai solusi elegan untuk masalah ini.
Strategy Pattern adalah salah satu Behavioral Design Pattern yang memungkinkan kamu mendefinisikan sekumpulan algoritma, membungkus masing-masing dalam kelas tersendiri, dan membuatnya bisa dipertukarkan satu sama lain. Dengan kata lain, kamu bisa mengganti “cara kerja” sebuah objek saat runtime tanpa mengubah kode utamanya.
Jika kamu baru mengenal dunia design patterns, ada baiknya membaca Mengenal Design Patterns: Fondasi Arsitektur Plugin yang Scalable sebagai dasar sebelum melanjutkan artikel ini.
Analogi Sederhana Strategy Pattern
Bayangkan kamu mau pergi dari Jakarta ke Bandung. Ada beberapa pilihan strategi perjalanan:
- Naik kereta — murah, nyaman, tapi tergantung jadwal
- Naik bus — fleksibel, banyak pilihan jam
- Naik mobil pribadi — bebas berhenti kapan saja, tapi macet di Puncak
Ketiga pilihan ini punya tujuan yang sama (sampai ke Bandung), tapi cara kerjanya berbeda. Kamu sebagai penumpang tidak perlu tahu detail cara mesin kereta bekerja atau cara sopir bus bernavigasi — kamu cukup memilih strategi yang paling cocok, lalu berangkat.
Itulah inti dari Strategy Pattern: pisahkan algoritma dari objek yang menggunakannya, sehingga algoritma bisa diganti-ganti tanpa merusak kode yang ada.
Memahami Konsep dan Komponen Utama
Strategy Pattern terdiri dari tiga komponen utama:
1. Strategy (Interface/Abstract Class)
Ini adalah “kontrak” yang mendefinisikan metode yang harus dimiliki oleh semua strategi. Semua strategi wajib mengimplementasikan interface ini.
2. Concrete Strategy
Ini adalah implementasi nyata dari strategy. Setiap kelas ini berisi algoritma yang berbeda, tapi semua mengikuti kontrak yang sama.
3. Context
Ini adalah kelas utama yang menggunakan strategi. Context menyimpan referensi ke sebuah strategy dan memanggilnya saat dibutuhkan. Context tidak perlu tahu detail implementasi strategi — dia cukup tahu bahwa strategi punya metode tertentu.
[Context] ──── menggunakan ────> [Strategy Interface]
▲
┌─────────────┼─────────────┐
│ │ │
[StrategyA] [StrategyB] [StrategyC]
Implementasi Strategy Pattern dengan Contoh Kode
Mari kita lihat implementasi nyata. Kita akan membuat sistem metode pembayaran — kasus yang sangat relevan jika kamu ingin membangun aplikasi e-commerce seperti Tokopedia atau Shopee.
Implementasi dengan Python
from abc import ABC, abstractmethod
# 1. Strategy Interface
class PaymentStrategy(ABC):
@abstractmethod
def pay(self, amount: float) -> str:
pass
# 2. Concrete Strategies
class CreditCardPayment(PaymentStrategy):
def __init__(self, card_number: str):
self.card_number = card_number
def pay(self, amount: float) -> str:
return f"Membayar Rp{amount:,.0f} dengan Kartu Kredit {self.card_number[-4:]}"
class GopayPayment(PaymentStrategy):
def __init__(self, phone_number: str):
self.phone_number = phone_number
def pay(self, amount: float) -> str:
return f"Membayar Rp{amount:,.0f} via GoPay ke {self.phone_number}"
class BankTransferPayment(PaymentStrategy):
def __init__(self, bank_name: str):
self.bank_name = bank_name
def pay(self, amount: float) -> str:
return f"Membayar Rp{amount:,.0f} via Transfer {self.bank_name}"
# 3. Context
class ShoppingCart:
def __init__(self):
self.items = []
self.payment_strategy: PaymentStrategy = None
def add_item(self, name: str, price: float):
self.items.append({"name": name, "price": price})
def set_payment_strategy(self, strategy: PaymentStrategy):
self.payment_strategy = strategy
def checkout(self):
total = sum(item["price"] for item in self.items)
if not self.payment_strategy:
return "Error: Pilih metode pembayaran dulu!"
return self.payment_strategy.pay(total)
# Penggunaan
cart = ShoppingCart()
cart.add_item("Laptop", 12_000_000)
cart.add_item("Mouse", 150_000)
# Pilih strategi: GoPay
cart.set_payment_strategy(GopayPayment("0812-3456-7890"))
print(cart.checkout())
# Output: Membayar Rp12.150.000 via GoPay ke 0812-3456-7890
# Ganti strategi: Kartu Kredit — tanpa mengubah kode ShoppingCart!
cart.set_payment_strategy(CreditCardPayment("1234-5678-9012-3456"))
print(cart.checkout())
# Output: Membayar Rp12.150.000 dengan Kartu Kredit 3456
# Ganti strategi: Transfer Bank
cart.set_payment_strategy(BankTransferPayment("BCA"))
print(cart.checkout())
# Output: Membayar Rp12.150.000 via Transfer BCA
Implementasi dengan JavaScript
// 1. Concrete Strategies (JavaScript pakai duck typing)
const creditCardStrategy = {
pay(amount) {
return `Membayar Rp${amount.toLocaleString('id-ID')} dengan Kartu Kredit`;
}
};
const gopayStrategy = {
pay(amount) {
return `Membayar Rp${amount.toLocaleString('id-ID')} via GoPay`;
}
};
const bankTransferStrategy = {
pay(amount) {
return `Membayar Rp${amount.toLocaleString('id-ID')} via Transfer Bank`;
}
};
// 2. Context
class ShoppingCart {
constructor() {
this.items = [];
this.paymentStrategy = null;
}
addItem(name, price) {
this.items.push({ name, price });
}
setPaymentStrategy(strategy) {
this.paymentStrategy = strategy;
}
checkout() {
const total = this.items.reduce((sum, item) => sum + item.price, 0);
if (!this.paymentStrategy) {
return 'Error: Pilih metode pembayaran!';
}
return this.paymentStrategy.pay(total);
}
}
// Penggunaan
const cart = new ShoppingCart();
cart.addItem('Sepatu', 500_000);
cart.addItem('Kaos', 150_000);
cart.setPaymentStrategy(gopayStrategy);
console.log(cart.checkout());
// Output: Membayar Rp650.000 via GoPay
cart.setPaymentStrategy(bankTransferStrategy);
console.log(cart.checkout());
// Output: Membayar Rp650.000 via Transfer Bank
cart.setPaymentStrategy(creditCardStrategy);
console.log(cart.checkout());
// Output: Membayar Rp650.000 dengan Kartu Kredit
Perhatikan bagaimana kita bisa mengganti metode pembayaran tanpa menyentuh kode ShoppingCart sama sekali. Inilah kekuatan Strategy Pattern.
Contoh Kasus Nyata
Strategy Pattern sangat berguna dalam berbagai skenario nyata:
1. Sorting Algorithm yang Bisa Diganti
from abc import ABC, abstractmethod
class SortStrategy(ABC):
@abstractmethod
def sort(self, data: list) -> list:
pass
class BubbleSortStrategy(SortStrategy):
def sort(self, data: list) -> list:
arr = data.copy()
n = len(arr)
for i in range(n):
for j in range(0, n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr
class QuickSortStrategy(SortStrategy):
def sort(self, data: list) -> list:
if len(data) <= 1:
return data
pivot = data[len(data) // 2]
left = [x for x in data if x < pivot]
middle = [x for x in data if x == pivot]
right = [x for x in data if x > pivot]
return self.sort(left) + middle + self.sort(right)
class DataProcessor:
def __init__(self, strategy: SortStrategy):
self.strategy = strategy
def process(self, data: list) -> list:
return self.strategy.sort(data)
# Data kecil: pakai Bubble Sort
processor = DataProcessor(BubbleSortStrategy())
print(processor.process([5, 3, 1, 4, 2]))
# Output: [1, 2, 3, 4, 5]
# Data besar: ganti ke Quick Sort tanpa ubah DataProcessor
processor.strategy = QuickSortStrategy()
print(processor.process([64, 25, 12, 22, 11]))
# Output: [11, 12, 22, 25, 64]
2. Sistem Diskon di Aplikasi E-Commerce
Bayangkan kamu membangun layanan seperti Tokopedia — kamu pasti perlu berbagai jenis diskon: diskon member, diskon flash sale, diskon kupon. Strategy Pattern sangat tepat di sini karena setiap jenis diskon bisa menjadi strategi tersendiri, dan kamu bisa menambah jenis diskon baru tanpa mengubah kode checkout.
from abc import ABC, abstractmethod
class DiscountStrategy(ABC):
@abstractmethod
def apply_discount(self, price: float) -> float:
pass
class NoDiscountStrategy(DiscountStrategy):
def apply_discount(self, price: float) -> float:
return price
class MemberDiscountStrategy(DiscountStrategy):
def apply_discount(self, price: float) -> float:
return price * 0.90 # Diskon 10% untuk member
class FlashSaleStrategy(DiscountStrategy):
def apply_discount(self, price: float) -> float:
return price * 0.70 # Diskon 30% flash sale
class CouponDiscountStrategy(DiscountStrategy):
def __init__(self, coupon_value: float):
self.coupon_value = coupon_value
def apply_discount(self, price: float) -> float:
return max(0, price - self.coupon_value)
class Product:
def __init__(self, name: str, price: float):
self.name = name
self.price = price
self.discount_strategy: DiscountStrategy = NoDiscountStrategy()
def set_discount(self, strategy: DiscountStrategy):
self.discount_strategy = strategy
def get_final_price(self) -> float:
return self.discount_strategy.apply_discount(self.price)
# Penggunaan
laptop = Product("Laptop Gaming", 15_000_000)
print(f"Harga normal: Rp{laptop.get_final_price():,.0f}")
# Output: Harga normal: Rp15.000.000
laptop.set_discount(MemberDiscountStrategy())
print(f"Harga member: Rp{laptop.get_final_price():,.0f}")
# Output: Harga member: Rp13.500.000
laptop.set_discount(FlashSaleStrategy())
print(f"Harga flash sale: Rp{laptop.get_final_price():,.0f}")
# Output: Harga flash sale: Rp10.500.000
laptop.set_discount(CouponDiscountStrategy(500_000))
print(f"Harga dengan kupon: Rp{laptop.get_final_price():,.0f}")
# Output: Harga dengan kupon: Rp14.500.000
Kelebihan dan Kekurangan Strategy Pattern
Kelebihan
| Kelebihan | Penjelasan |
|---|---|
| Open/Closed Principle | Tambah strategi baru tanpa mengubah kode yang sudah ada |
| Single Responsibility | Setiap kelas strategi fokus pada satu algoritma saja |
| Mudah diuji | Setiap strategi bisa diuji secara independen |
| Fleksibel | Strategi bisa diganti saat runtime sesuai kebutuhan |
| Mengurangi kondisional | Menggantikan blok if-else yang panjang dan sulit dibaca |
Kekurangan
| Kekurangan | Penjelasan |
|---|---|
| Jumlah kelas bertambah | Setiap strategi memerlukan kelas tersendiri |
| Overkill untuk kasus sederhana | Jika hanya ada 2 strategi yang tidak akan berubah, if-else mungkin lebih praktis |
| Client harus memilih strategi | Kode yang memanggil Context harus tahu strategi mana yang tepat digunakan |
Pertanyaan yang Sering Diajukan
Apa perbedaan Strategy Pattern dengan menggunakan if-else biasa?
Dengan if-else, semua logika algoritma bercampur dalam satu kelas atau fungsi. Ketika kamu menambah pilihan baru, kamu harus masuk ke kode lama dan mengubahnya — ini berisiko menimbulkan bug. Strategy Pattern memisahkan setiap algoritma ke kelas tersendiri, sehingga menambah strategi baru tidak mengganggu kode yang sudah berjalan.
Kapan sebaiknya menggunakan Strategy Pattern?
Gunakan Strategy Pattern ketika kamu punya beberapa cara berbeda untuk melakukan satu tugas yang sama dan ingin bisa menukar cara tersebut secara dinamis. Tanda-tandanya: kode kamu penuh if-else untuk memilih algoritma, atau kamu ingin menambah variasi perilaku tanpa mengubah kelas utama.
Apa perbedaan Strategy Pattern dengan Decorator Pattern?
Strategy Pattern mengganti keseluruhan algoritma atau perilaku sebuah objek. Sementara Decorator Pattern menambahkan perilaku baru di atas perilaku yang sudah ada, seperti lapisan tambahan. Analogi: Strategy adalah memilih pakai motor atau mobil, Decorator adalah menambahkan GPS ke kendaraan yang sudah dipilih.
Apakah Strategy Pattern hanya bisa digunakan di OOP?
Tidak. Meski Strategy Pattern berasal dari dunia Object-Oriented Programming, konsepnya bisa diterapkan dalam paradigma fungsional menggunakan fungsi tingkat tinggi (higher-order functions). Di JavaScript dan Python, kamu bisa langsung mengoper fungsi sebagai strategi tanpa perlu membuat kelas sama sekali.
Apakah Strategy Pattern memperlambat performa aplikasi?
Untuk sebagian besar kasus, dampak performa dari Strategy Pattern sangat kecil dan tidak terasa. Manfaat berupa kode yang lebih mudah dipelihara dan dikembangkan jauh lebih besar daripada overhead yang mungkin timbul. Fokuslah pada keterbacaan dan kemudahan pemeliharaan kode terlebih dahulu.
Kesimpulan
Strategy Pattern adalah pola desain yang sangat praktis untuk mengelola beragam algoritma atau perilaku dalam kode kamu. Dengan memisahkan setiap strategi ke dalam kelas tersendiri dan menyatukan mereka lewat sebuah interface, kamu mendapatkan kode yang:
- Mudah dikembangkan — tambah strategi baru tanpa ubah kode lama
- Mudah diuji — setiap strategi bisa diuji secara mandiri
- Mudah dibaca — setiap kelas punya tanggung jawab yang jelas
Kunci utama yang perlu diingat: Context tidak perlu tahu detail strategi, dia cukup tahu cara memanggilnya. Sama seperti penumpang kereta yang tidak perlu tahu cara masinis menjalankan lokomotif — dia cukup duduk dan menikmati perjalanan.
Untuk melanjutkan pemahaman kamu tentang arsitektur kode yang bersih, coba eksplorasi Panduan Lengkap Tipe Data dan Operator JavaScript untuk memperkuat fondasi kode JavaScript kamu sebelum mengimplementasikan pola-pola ini lebih lanjut.
Selamat belajar dan terus berlatih! Jika ada pertanyaan atau kamu ingin melihat contoh implementasi lain dari Strategy Pattern, jangan ragu untuk menjelajahi artikel-artikel lainnya di KamusNgoding — kita belajar bersama!