Langsung ke konten
KamusNgoding
Pemula Behavioral 5 menit baca

Apa itu Strategy Pattern? Penjelasan Lengkap untuk Pemula

#design pattern #behavioral #strategy #pemula #oop

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

KelebihanPenjelasan
Open/Closed PrincipleTambah strategi baru tanpa mengubah kode yang sudah ada
Single ResponsibilitySetiap kelas strategi fokus pada satu algoritma saja
Mudah diujiSetiap strategi bisa diuji secara independen
FleksibelStrategi bisa diganti saat runtime sesuai kebutuhan
Mengurangi kondisionalMenggantikan blok if-else yang panjang dan sulit dibaca

Kekurangan

KekuranganPenjelasan
Jumlah kelas bertambahSetiap strategi memerlukan kelas tersendiri
Overkill untuk kasus sederhanaJika hanya ada 2 strategi yang tidak akan berubah, if-else mungkin lebih praktis
Client harus memilih strategiKode 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!

Artikel Terkait