Strategy pattern w praktyce

Strategy pattern pozwala zdefiniować rodzinę algorytmów, enkapsulować każdy z nich i sprawiać że są wymienne. Dzięki temu możesz zmieniać logikę działania aplikacji bez modyfikacji kodu klienta.

Dlaczego Strategy pattern jest ważny?

W prawdziwych aplikacjach często mamy sytuacje gdzie ten sam problem można rozwiązać na kilka sposobów. Na przykład – sortowanie listy może odbywać się przez bubble sort, quick sort czy merge sort. Bez Strategy pattern kod szybko staje się nieczytelny przez mnogie instrukcje if-else lub switch.

Strategy pattern rozwiązuje ten problem przez separację algorytmu od kodu który go używa. To znacznie ułatwia dodawanie nowych algorytmów i testowanie różnych approaches.

Co się nauczysz:

  • Jak zaimplementować Strategy pattern w Javie 8
  • Kiedy używać tego wzorca w prawdziwych projektach
  • Jak uniknąć typowych błędów przy implementacji
  • Różnice między Strategy a innymi wzorcami behawioralnymi
  • Praktyczne przykłady z e-commerce i systemów płatności

Wymagania wstępne:

  • Znajomość podstaw Javy 8 (klasy, interfejsy, lambda expressions)
  • Podstawowe zrozumienie wzorców projektowych
  • Doświadczenie z programowaniem obiektowym

Czym jest Strategy pattern?

Strategy pattern to wzorzec behawioralny który definiuje rodzinę algorytmów, enkapsuluje każdy z nich w osobnej klasie i czyni je wymienialnymi. Kluczowe komponenty to:

Context – klasa która używa strategii
Strategy – interfejs definiujący wspólny kontrakt
ConcreteStrategy – konkretne implementacje algorytmów

Implementacja w Javie 8

Pokażmy praktyczny przykład z systemem obliczania rabatów w sklepie internetowym:

// Interfejs strategii
public interface DiscountStrategy {
    double calculateDiscount(double amount);
}

// Konkretne strategie
public class RegularCustomerDiscount implements DiscountStrategy {
    @Override
    public double calculateDiscount(double amount) {
        return amount * 0.05; // 5% rabatu
    }
}

public class PremiumCustomerDiscount implements DiscountStrategy {
    @Override
    public double calculateDiscount(double amount) {
        return amount * 0.15; // 15% rabatu
    }
}

public class VipCustomerDiscount implements DiscountStrategy {
    @Override
    public double calculateDiscount(double amount) {
        return amount * 0.25; // 25% rabatu
    }
}

Teraz klasa Context która używa strategii:

public class ShoppingCart {
    private DiscountStrategy discountStrategy;
    
    public ShoppingCart(DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }
    
    public void setDiscountStrategy(DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }
    
    public double calculateTotal(double amount) {
        double discount = discountStrategy.calculateDiscount(amount);
        return amount - discount;
    }
}

Użycie w praktyce

public class ECommerceDemo {
    public static void main(String[] args) {
        double orderAmount = 1000.0;
        
        // Zwykły klient
        ShoppingCart cart1 = new ShoppingCart(new RegularCustomerDiscount());
        System.out.println("Regular customer pays: " + cart1.calculateTotal(orderAmount));
        
        // Premium klient  
        ShoppingCart cart2 = new ShoppingCart(new PremiumCustomerDiscount());
        System.out.println("Premium customer pays: " + cart2.calculateTotal(orderAmount));
        
        // Zmiana strategii w runtime
        cart1.setDiscountStrategy(new VipCustomerDiscount());
        System.out.println("Now VIP price: " + cart1.calculateTotal(orderAmount));
    }
}
Pro tip: W Javie 8 możesz używać lambda expressions dla prostych strategii: cart.setDiscountStrategy(amount -> amount * 0.1)

Kiedy używać Strategy pattern?

Strategy pattern świetnie sprawdza się gdy:

– Masz kilka sposobów wykonania tego samego zadania
– Chcesz uniknąć długich instrukcji if-else lub switch
– Algoritmy mogą się zmieniać w runtime
– Testujesz różne approaches i chcesz łatwo je wymieniać

Strategy pattern to jak wybieranie środka transportu – możesz jechać autem, pociągiem lub samolotem. Cel jest ten sam (dotarcie do miejsca), ale sposób się różni.

Typowe błędy początkujących

Błąd #1: Tworzenie zbyt wielu strategii dla prostych operacji – czasem zwykłe if-else jest lepsze
Błąd #2: Przekazywanie zbyt wielu parametrów do strategii – lepiej utworzyć obiekt z danymi
Błąd #3: Mieszanie Strategy z Factory pattern – to różne wzorce o różnych celach

Strategy vs inne wzorce

WzorzecCelKiedy używać
StrategyWymienne algorytmyRóżne sposoby robienia tego samego
StateZmiana zachowaniaObiekt zmienia sposób działania
CommandEnkapsulacja operacjiKolejkowanie, logowanie operacji
Czy Strategy pattern spowalnia aplikację?

Nie znacząco. Overhead to jedno dodatkowe wywołanie metody, ale zyskujesz na czytelności i elastyczności kodu.

Kiedy nie używać Strategy pattern?

Gdy masz tylko jeden algorytm lub różnice są minimalne. Nie komplikuj kodu bez potrzeby.

Czy mogę używać enum zamiast klas?

Tak, w Javie 8 enum może implementować interfejsy i mieć metody. To dobra opcja dla prostych strategii.

Jak wybierać strategię w runtime?

Możesz użyć Factory pattern lub Map żeby mapować nazwy na konkretne implementacje.

Czy Strategy pattern można łączyć z innymi wzorcami?

Tak! Często używa się go z Factory (do tworzenia strategii) lub Decorator (do modyfikowania zachowania).

Przydatne zasoby:

🚀 Zadanie dla Ciebie

Zaimplementuj system płatności używając Strategy pattern. Stwórz strategie dla płatności kartą, PayPal i przelewy bankowe. Każda strategia powinna mieć różne opłaty za transakcję. Przetestuj zmienianie strategii w runtime.

Czy używałeś już Strategy pattern w swoich projektach? Jakie inne algorytmy wymienialne implementowałeś? Podziel się doświadczeniem w komentarzach!

Zostaw komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Przewijanie do góry