DESIGN-PATTERNS SOFTWARE-DESIGN CLEAN-CODE FLEXIBILITY MAINTAINABILITY OBJECT-ORIENTED-PROGRAMMING JAVASCRIPT TYPESCRIPT BACKEND-DEVELOPMENT FRONTEND-DEVELOPMENT REFACTORING

Pola Strategi (Strategy Pattern): Jurus Rahasia Kode Fleksibel dan Mudah Diubah di Aplikasi Web

⏱️ 13 menit baca
👨‍💻

Pola Strategi (Strategy Pattern): Jurus Rahasia Kode Fleksibel dan Mudah Diubah di Aplikasi Web

1. Pendahuluan

Pernahkah kamu menemukan dirimu tenggelam dalam lautan if/else if/else atau switch yang panjang dan rumit di dalam kode? 🤔 Bayangkan skenario di mana kamu harus menangani berbagai metode pembayaran, strategi diskon, atau algoritma validasi yang berbeda. Setiap kali ada perubahan atau penambahan metode baru, kamu harus mengotak-atik blok kode yang sama, berisiko menciptakan bug dan membuat kode semakin sulit dipahami. Ini adalah “if/else hell” yang sering menghantui developer!

Masalah utama di sini adalah ketika logika bisnis yang bervariasi dicampur aduk dalam satu fungsi atau kelas. Kode menjadi kaku, sulit diuji, dan melanggar prinsip Open/Closed Principle (O dari SOLID) – di mana entitas software harus terbuka untuk ekstensi, tapi tertutup untuk modifikasi.

Nah, di sinilah Pola Strategi (Strategy Pattern) datang sebagai pahlawan! 💪 Ini adalah salah satu pola desain perilaku (behavioral design pattern) yang memungkinkan kita untuk mendefinisikan serangkaian algoritma, menempatkan masing-masing algoritma ke dalam kelas terpisah, dan membuat objeknya dapat dipertukarkan. Dengan kata lain, ia memungkinkan klien untuk memilih algoritma mana yang akan digunakan saat runtime, tanpa harus mengubah kode inti yang menggunakan algoritma tersebut.

Dengan mengimplementasikan Strategy Pattern, kita bisa mendapatkan kode yang lebih:

Mari kita selami lebih dalam bagaimana pola ini bekerja dan bagaimana kamu bisa menerapkannya dalam proyek web developmentmu!

2. Memahami Inti Strategy Pattern: Konteks, Strategi, dan Strategi Konkret

Untuk memahami Strategy Pattern, bayangkan sebuah aplikasi navigasi. Kamu ingin pergi dari titik A ke titik B. Ada banyak cara untuk sampai ke sana: naik mobil, naik sepeda, atau berjalan kaki. Setiap metode memiliki algoritma perhitungannya sendiri (misalnya, mobil butuh jalan raya, sepeda bisa lewat jalur khusus, jalan kaki bisa lewat gang).

Dalam analogi ini:

Ketika kamu ingin pergi, kamu hanya perlu memberi tahu aplikasi navigasi “gunakan strategi mobil” atau “gunakan strategi sepeda”, dan aplikasi akan menjalankan algoritma yang sesuai tanpa mengubah kode intinya.

📌 Komponen Utama Strategy Pattern:

  1. Strategy (Antarmuka Strategi): Mendefinisikan antarmuka umum untuk semua algoritma yang didukung oleh Konteks. Klien menggunakan antarmuka ini untuk mengakses strategi.
  2. Concrete Strategy (Strategi Konkret): Mengimplementasikan antarmuka Strategi dengan algoritma tertentu. Ada banyak strategi konkret yang berbeda.
  3. Context (Konteks): Memegang referensi ke objek Strategi dan berinteraksi dengannya melalui antarmuka Strategi. Konteks tidak tahu implementasi spesifik dari strategi, hanya tahu cara menggunakannya. Ia bisa menerima objek strategi dari klien atau membuatnya sendiri.

Mari kita lihat contoh nyatanya dalam kode.

3. Contoh Kasus 1: Pemrosesan Pembayaran di Backend

Ini adalah salah satu skenario klasik di mana Strategy Pattern bersinar. Aplikasi e-commerce modern seringkali terintegrasi dengan berbagai penyedia pembayaran (payment gateways) seperti PayPal, Stripe, Midtrans, Doku, dll. Setiap gateway memiliki API dan alur yang berbeda.

❌ Sebelum Strategy Pattern (Anti-Pattern):

// paymentService.ts
class PaymentService {
    processPayment(amount: number, method: string, details: any): boolean {
        if (method === 'paypal') {
            console.log(`Processing PayPal payment of ${amount} with details:`, details);
            // Logika integrasi PayPal yang kompleks...
            return true;
        } else if (method === 'stripe') {
            console.log(`Processing Stripe payment of ${amount} with details:`, details);
            // Logika integrasi Stripe yang kompleks...
            return true;
        } else if (method === 'midtrans') {
            console.log(`Processing Midtrans payment of ${amount} with details:`, details);
            // Logika integrasi Midtrans yang kompleks...
            return true;
        } else {
            console.error(`Unknown payment method: ${method}`);
            return false;
        }
    }
}

// Penggunaan
const service = new PaymentService();
service.processPayment(100, 'paypal', { email: 'user@example.com' });
service.processPayment(250, 'stripe', { cardToken: 'xyz123' });
service.processPayment(50, 'unknown', {});

⚠️ Masalah:

✅ Dengan Strategy Pattern:

Pertama, kita definisikan antarmuka untuk strategi pembayaran:

// paymentStrategy.ts
interface PaymentStrategy {
    processPayment(amount: number, details: any): boolean;
}

Kemudian, kita buat strategi konkret untuk setiap metode pembayaran:

// paypalPaymentStrategy.ts
class PayPalPaymentStrategy implements PaymentStrategy {
    processPayment(amount: number, details: any): boolean {
        console.log(`[PayPal] Memproses pembayaran sebesar Rp${amount} dengan email: ${details.email}`);
        // Logika integrasi API PayPal
        // ...
        console.log("[PayPal] Pembayaran berhasil.");
        return true;
    }
}

// stripePaymentStrategy.ts
class StripePaymentStrategy implements PaymentStrategy {
    processPayment(amount: number, details: any): boolean {
        console.log(`[Stripe] Memproses pembayaran sebesar Rp${amount} dengan token kartu: ${details.cardToken}`);
        // Logika integrasi API Stripe
        // ...
        console.log("[Stripe] Pembayaran berhasil.");
        return true;
    }
}

// midtransPaymentStrategy.ts
class MidtransPaymentStrategy implements PaymentStrategy {
    processPayment(amount: number, details: any): boolean {
        console.log(`[Midtrans] Memproses pembayaran sebesar Rp${amount} dengan order ID: ${details.orderId}`);
        // Logika integrasi API Midtrans
        // ...
        console.log("[Midtrans] Pembayaran berhasil.");
        return true;
    }
}

Terakhir, kita buat kelas Konteks yang akan menggunakan strategi ini:

// paymentProcessor.ts
class PaymentProcessor {
    private strategy: PaymentStrategy;

    constructor(strategy: PaymentStrategy) {
        this.strategy = strategy;
    }

    setStrategy(strategy: PaymentStrategy): void {
        this.strategy = strategy;
    }

    executePayment(amount: number, details: any): boolean {
        console.log("------------------------------------");
        console.log(`Mencoba memproses pembayaran...`);
        const result = this.strategy.processPayment(amount, details);
        console.log(`Status pembayaran: ${result ? 'Berhasil' : 'Gagal'}`);
        console.log("------------------------------------");
        return result;
    }
}

// Penggunaan
const payPalStrategy = new PayPalPaymentStrategy();
const stripeStrategy = new StripePaymentStrategy();
const midtransStrategy = new MidtransPaymentStrategy();

const processor = new PaymentProcessor(payPalStrategy); // Default ke PayPal

processor.executePayment(150000, { email: 'buyer@example.com' });

processor.setStrategy(stripeStrategy); // Ganti strategi ke Stripe
processor.executePayment(200000, { cardToken: 'stripe_token_456' });

processor.setStrategy(midtransStrategy); // Ganti strategi ke Midtrans
processor.executePayment(75000, { orderId: 'ORD-789' });

// Menambah strategi baru (misal: OVO) tidak akan mengubah kelas PaymentProcessor!
// Cukup buat OVOPaymentStrategy dan instansiasi.

💡 Manfaat:

4. Contoh Kasus 2: Validasi Formulir Dinamis di Frontend

Strategy Pattern juga sangat berguna di frontend, terutama untuk validasi input. Bayangkan kamu memiliki formulir dengan berbagai jenis input yang membutuhkan aturan validasi yang berbeda: email, password, angka, teks wajib isi, dll.

❌ Sebelum Strategy Pattern (Anti-Pattern):

// formValidator.ts (di frontend)
function validateInput(type: string, value: string): string | null {
    if (type === 'email') {
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        if (!emailRegex.test(value)) {
            return 'Format email tidak valid.';
        }
    } else if (type === 'password') {
        if (value.length < 8) {
            return 'Password minimal 8 karakter.';
        }
        if (!/[A-Z]/.test(value)) {
            return 'Password harus mengandung huruf kapital.';
        }
    } else if (type === 'required') {
        if (!value.trim()) {
            return 'Field ini wajib diisi.';
        }
    }
    // ... dan seterusnya untuk jenis validasi lain
    return null; // Valid
}

// Penggunaan di komponen React/Vue/Angular
const emailError = validateInput('email', 'invalid-email');
const passwordError = validateInput('password', 'short');
const requiredError = validateInput('required', '');

⚠️ Masalah:

✅ Dengan Strategy Pattern:

Definisikan antarmuka validasi:

// validationStrategy.ts
interface ValidationStrategy {
    validate(value: string): string | null;
}

Buat strategi konkret untuk setiap aturan validasi:

// emailValidationStrategy.ts
class EmailValidationStrategy implements ValidationStrategy {
    validate(value: string): string | null {
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return emailRegex.test(value) ? null : 'Format email tidak valid.';
    }
}

// passwordValidationStrategy.ts
class PasswordValidationStrategy implements ValidationStrategy {
    validate(value: string): string | null {
        if (value.length < 8) {
            return 'Password minimal 8 karakter.';
        }
        if (!/[A-Z]/.test(value)) {
            return 'Password harus mengandung huruf kapital.';
        }
        return null;
    }
}

// requiredValidationStrategy.ts
class RequiredValidationStrategy implements ValidationStrategy {
    validate(value: string): string | null {
        return value.trim() !== '' ? null : 'Field ini wajib diisi.';
    }
}

Konteks FormValidator yang akan menggunakan strategi ini:

// formValidator.ts
class FormValidator {
    private strategies: ValidationStrategy[];

    constructor(strategies: ValidationStrategy[]) {
        this.strategies = strategies;
    }

    addStrategy(strategy: ValidationStrategy): void {
        this.strategies.push(strategy);
    }

    validate(value: string): string[] {
        const errors: string[] = [];
        for (const strategy of this.strategies) {
            const error = strategy.validate(value);
            if (error) {
                errors.push(error);
            }
        }
        return errors;
    }
}

// Penggunaan di komponen React/Vue/Angular
const emailValidator = new FormValidator([
    new RequiredValidationStrategy(),
    new EmailValidationStrategy()
]);

const passwordValidator = new FormValidator([
    new RequiredValidationStrategy(),
    new PasswordValidationStrategy()
]);

console.log('Validasi Email "user@test.com":', emailValidator.validate('user@test.com')); // []
console.log('Validasi Email "invalid":', emailValidator.validate('invalid')); // ["Format email tidak valid."]
console.log('Validasi Password "Pass123":', passwordValidator.validate('Pass123')); // ["Password minimal 8 karakter."]
console.log('Validasi Password "password":', passwordValidator.validate('password')); // ["Password harus mengandung huruf kapital."]

💡 Manfaat:

5. Kapan Menggunakan Strategy Pattern?

🎯 Gunakan Strategy Pattern ketika:

❌ Hindari Strategy Pattern ketika:

6. Kelebihan dan Kekurangan

Kelebihan

Kekurangan

Kesimpulan

Pola Strategi (Strategy Pattern) adalah alat yang sangat kuat dalam kotak perkakas seorang developer untuk membangun aplikasi web yang lebih fleksibel, mudah diubah, dan maintainable. Dengan memisahkan algoritma yang bervariasi ke dalam kelas-kelas strategi terpisah, kamu bisa mengucapkan selamat tinggal pada if/else hell dan menyambut kode yang lebih bersih, mudah diuji, dan skalabel.

Meskipun membutuhkan sedikit usaha ekstra di awal, manfaat jangka panjangnya dalam hal kemudahan perawatan dan adaptasi terhadap perubahan bisnis akan jauh melampaui biaya tersebut. Jadi, lain kali kamu menghadapi skenario dengan banyak perilaku yang mirip namun berbeda, pertimbangkan untuk menerapkan Strategy Pattern! Kode Anda (dan rekan tim Anda) akan berterima kasih.

🔗 Baca Juga