Wzorzec projektowy: Strategia


Wzorzec Strategia jako jeden z wzorców behawioralnych pozwala definiować różne algorytmy lub zachowania (np. sposób sortowania, obliczania rabatu) i wybierać je w czasie działania programu, przekazując je jako obiekty. Dzięki temu łatwo możemy podmienić „strategię” bez zmiany kodu głównej klasy.

Typowy przykład: System płatności, gdzie możesz przekazywać różne sposoby płatności (np. karta, PayPal, przelew) jako strategię.

Zastosowanie i charakterystyka

Wzorzec projektowy Strategia (Strategy) pozwala na wydzielenie zestawu algorytmów lub zachowań do osobnych klas i dynamiczne wybieranie jednego z nich w trakcie działania programu. Głównym celem jest ułatwienie rozbudowy oraz testowania kodu poprzez uniezależnienie obiektu kontekstu od szczegółów implementacyjnych strategii.

  • Strategia to wzorzec behawioralny, który polega na delegowaniu określonego zachowania do obiektu pośredniego („strategii”) zgodnie z ustalonym interfejsem.
  • Najczęściej stosowany, gdy chcemy dynamicznie zmieniać algorytm wykonujący określoną funkcjonalność, zachowując spójność interfejsu komunikacji z kontekstem.
  • W PHP coraz popularniejszym substytutem dla strategii są funkcje anonimowe, ale wzorzec nadal pozostaje czytelny w bardziej złożonych przypadkach.

Przykład implementacji strategii w PHP

Przykład 1: Rabaty w sklepie internetowym

interface DiscountStrategy {
    public function calculate(float $amount): float;
}

class FixedDiscount implements DiscountStrategy {
    public function calculate(float $amount): float {
        return $amount - 10;
    }
}

class PercentageDiscount implements DiscountStrategy {
    public function calculate(float $amount): float {
        return $amount * 0.8;
    }
}

class Order {
    private DiscountStrategy $discountStrategy;
    private float $amount;

    public function __construct(float $amount, DiscountStrategy $discountStrategy) {
        $this->amount = $amount;
        $this->discountStrategy = $discountStrategy;
    }

    public function getTotal(): float {
        return $this->discountStrategy->calculate($this->amount);
    }
}

// Użycie
$order = new Order(100, new FixedDiscount());
echo $order->getTotal(); // 90

$order = new Order(100, new PercentageDiscount());
echo $order->getTotal(); // 80

W powyższym przykładzie możemy łatwo podmieniać strategię naliczania rabatu bez modyfikowania klasy Order

Przykład 2: Transport na lotnisko

interface TransportStrategy {
    public function transport(User $user): TransportResult;
}

class CityBusTransport implements TransportStrategy {
    public function transport(User $user): TransportResult {
        // Transport busem
        return new TransportResult();
    }
}

class PersonalCarTransport implements TransportStrategy {
    public function transport(User $user): TransportResult {
        // Transport prywatnym autem
        return new TransportResult();
    }
}

class TransportationToAirport {
    private TransportStrategy $strategy;

    public function __construct(TransportStrategy $strategy) {
        $this->strategy = $strategy;
    }

    public function run(User $user): TransportResult {
        return $this->strategy->transport($user);
    }
}

Możesz dynamicznie przypisywać różne strategie transportu, co ułatwia rozbudowę i testowanie systemu.

Gdzie najczęściej stosować?

  • Konkretne algorytmy, które mogą się różnić (sortowanie, rabaty, płatności)
  • Różne sposoby realizacji podobnego zadania (kompresja, generowanie ID, logika płatnicza)

Wzorzec Strategia jest prosty, przejrzysty i wyjątkowo przydatny przy rozbudowanych, testowalnych systemach, które muszą być gotowe na częste zmiany algorytmów