Menu
Coddy logo textTech

Wzorzec Strategy

Część sekcji Programowanie obiektowe ścieżki PHP w Coddy. Lekcja 74 z 91.

Wzorzec Strategii to behawioralny wzorzec projektowy, który pozwala zdefiniować rodzinę algorytmów, zamknąć każdy z nich w osobnej klasie i sprawić, by można je było wymieniać w czasie działania programu. Obiekt korzystający ze strategii nie musi wiedzieć, który algorytm jest uruchamiany.

Wyobraź sobie koszyk zakupowy, który może stosować różne strategie rabatowe. Zamiast zakodowywać logikę rabatów na stałe za pomocą instrukcji warunkowych, definiujesz każdy rabat jako osobną klasę:

<?php
interface DiscountStrategy {
    public function calculate(float $total): float;
}

class NoDiscount implements DiscountStrategy {
    public function calculate(float $total): float {
        return $total;
    }
}

class PercentageDiscount implements DiscountStrategy {
    public function __construct(private int $percent) {}
    
    public function calculate(float $total): float {
        return $total - ($total * $this->percent / 100);
    }
}

class FixedDiscount implements DiscountStrategy {
    public function __construct(private float $amount) {}
    
    public function calculate(float $total): float {
        return max(0, $total - $this->amount);
    }
}

Klasa kontekstu przyjmuje dowolną strategię i deleguje obliczenia:

<?php
class ShoppingCart {
    public function __construct(private DiscountStrategy $discount) {}
    
    public function setDiscount(DiscountStrategy $discount): void {
        $this->discount = $discount;
    }
    
    public function checkout(float $total): float {
        return $this->discount->calculate($total);
    }
}

$cart = new ShoppingCart(new NoDiscount());
echo $cart->checkout(100) . "\n";

$cart->setDiscount(new PercentageDiscount(20));
echo $cart->checkout(100);

Wynik:

100
80

Wzorzec strategii sprawdza się, gdy istnieje wiele sposobów wykonania operacji i chcesz przełączać się między nimi bez modyfikowania klasy, która z nich korzysta. Dodanie nowego typu rabatu oznacza utworzenie nowej klasy, a nie zmianę istniejącego kodu.

challenge icon

Wyzwanie

Łatwy

Zbudujmy kalkulator kosztów wysyłki z użyciem wzorca strategii. Różne metody wysyłki mają różne zasady naliczania opłat: standardowa wysyłka pobiera stałą opłatę, ekspresowa dolicza opłatę zależną od wagi, a wysyłka następnego dnia łączy oba podejścia. Wzorzec strategii pozwala podmieniać te obliczenia bez zmieniania kodu, który z nich korzysta.

Podzielisz kod na cztery pliki:

  • ShippingStrategy.php: Utwórz interfejs ShippingStrategy, który określa kontrakt dla wszystkich metod wysyłki. Powinien zawierać metodę calculate(float $weight): float, która przyjmuje wagę paczki i zwraca koszt wysyłki.
  • ShippingMethods.php: Dołącz plik ShippingStrategy i utwórz trzy klasy implementujące ten interfejs:
    • StandardShipping: Pobiera stałą opłatę w wysokości $5.00, niezależnie od wagi
    • ExpressShipping: Pobiera opłatę $2.00 za kilogram (pomnóż wagę przez 2)
    • OvernightShipping: Pobiera opłatę podstawową $10.00 plus $3.00 za kilogram
  • ShippingCalculator.php: Dołącz plik ShippingStrategy i utwórz klasę ShippingCalculator, która pełni rolę kontekstu. Twój kalkulator powinien:
    • Przyjmować ShippingStrategy w konstruktorze (użyj promocji właściwości konstruktora)
    • Zawierać metodę setStrategy(ShippingStrategy $strategy): void, która pozwala zmienić metodę wysyłki w trakcie działania programu
    • Zawierać metodę calculateCost(float $weight): float, która deleguje obliczenia do bieżącej strategii
  • main.php: Dołącz pliki ShippingMethods i ShippingCalculator. Otrzymasz dwa dane wejściowe: typ wysyłki ("standard", "express" lub "overnight") oraz wagę paczki.

    Utwórz obiekt ShippingCalculator, ustawiając początkowo StandardShipping. Na podstawie typu wysyłki podanego jako dane wejściowe użyj setStrategy(), aby przełączyć się na odpowiednią metodę wysyłki. Następnie oblicz koszt dla podanej wagi i wyświetl wynik z dokładnością do dwóch miejsc po przecinku.

    Wynik powinien mieć postać: Shipping cost: $[cost]

To wyzwanie pokazuje, jak wzorzec strategii zwiększa elastyczność systemu wysyłki: dodanie nowej metody wysyłki, takiej jak SameDayShipping, wymaga jedynie utworzenia nowej klasy implementującej interfejs, bez modyfikowania kalkulatora ani istniejących metod wysyłki.

Spróbuj swoich sił

<?php
require_once 'ShippingMethods.php';
require_once 'ShippingCalculator.php';

// Odczytaj dane wejściowe
$shippingType = trim(fgets(STDIN));
$weight = floatval(fgets(STDIN));

// TODO: Utwórz ShippingCalculator z początkową strategią StandardShipping

// TODO: Na podstawie podanego typu wysyłki ("standard", "express" lub "overnight"),
// użyj setStrategy(), aby wybrać odpowiednią metodę wysyłki

// TODO: Oblicz koszt dla podanej wagi

// TODO: Wyświetl wynik sformatowany do dwóch miejsc po przecinku
// Format wyjściowy: Shipping cost: $[cost]

?>
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator PHP online