Wzorce projektowe, cz. 2 Strategy
pobierz w .pdf
(przeznaczone do wydruku)
Druga część z serii wpisów o wzorcach projektowych. Dziś omówię wzorzec Strategii (Strategy).
Strategia jest wzorcem projektowym, który definiuje rodzinę wymiennych algorytmów i kapsułkuje je w postaci klas. Dzięki temu umożliwia wymienne stosowanie każdego z nich w trakcie działania programu.

Diagram klas wzorca Strategy
Przykładowa implementacja
<?php interface AbstractStrategy{ function task(); } class ConcreteStrategy1 implements AbstractStrategy{ public function task() { echo "Strategy 1"; } } class ConcreteStrategy2 implements AbstractStrategy{ public function task() { echo "Strategy 2"; } } class Context{ private $strategy; public function setStrategy(AbstractStrategy $obj) { $this->strategy=$obj; } public function getStrategy() { return $this->strategy; } } // testy $obj = new Context(); $obj->setStrategy(new ConcreteStrategy1); $obj->getStrategy()->task(); // wyswietla „Strategy 1” $obj->setStrategy(new ConcreteStrategy2); $obj->getStrategy()->task(); // wyswietla „Strategy 2” ?>
We wzorcu tym definiujemy wspólny interfejs dla wszystkich strategii. Następnie, w poszczególnych klasach implementujemy metody z konkretnymi już algorytmami. Za pomocą klasy Context możemy łatwo zmieniać używaną strategię w trakcie działania aplikacji.
Przykład z życia wzięty
Przypuśćmy, że tworzymy sklep internetowy oferujący swoje usługi w kilku państwach. Jak wiadomo prawo podatkowe znacząco różni się w poszczególnych krajach. Powstaje problem naliczenia odpowiedniego podatku dla klientów pochodzących z odmiennych państw. Jak to rozwiązać? Wybrać odpowiednią stawkę za pomocą licznych instrukcji warunkowych? Nie, do tego świetnie nadaje się wzorzec strategii.
<?php interface Tax{ public function count($net); } class TaxPL implements Tax{ public function count($net) { return 0.23*$net; } } class TaxEN implements Tax{ public function count($net) { return 0.15*$net; } } class TaxDE implements Tax{ public function count($net) { return 0.3*$net; } } class Context{ private $tax; /** * @return mixed */ public function getTax() { return $this->tax; } /** * @param mixed $tax */ public function setTax(Tax $tax) { $this->tax = $tax; } } // testy $tax = new Context(); $tax->setTax(new TaxPL()); echo $tax->getTax()->count(100); // wyswietla "23" $tax->setTax(new TAXEN()); echo $tax->getTax()->count(100); // wyswietla "15" $tax->setTax(new TAXDE()); echo $tax->getTax()->count(100); // wyswietla "30" ?>
Za pomocą metody setTax() możemy w bardzo prosty sposób ustawić odpowiednią strategię obliczania podatku dostosowaną do państwa, z którego pochodzi nasz klient. Jeżeli sklep zdecyduje się na poszerzenie działalności o kolejny kraj, wystarczy tylko dopisać odpowiednią klasę implementującą interfejs Tax.
Zalety i wady
Zalety:
- Eliminacja instrukcji warunkowych – kod jest bardziej przejrzysty.
- Umożliwia wybór implementacji – algorytmy mogą rozwiązywać ten sam problem, lecz różnić się uzyskiwanymi korzyściami.
- Łatwość dołączania kolejnych strategii.
- Łatwiejsze testowanie programu – można debugować każdą strategię z osobna.
Wady:
- Dodatkowy koszt komunikacji między klientem, a strategią (wywołania metod, przekazywanie danych).
- „Rozmycie” kodu na kilka klas.
Zastosowanie
Wszędzie tam, gdzie istnieje potrzeba rozwiązania danego problemu na kilka różnych sposobów.
Oficjalna wirtualna maszyna Javy HotSpot wykorzystuje wzorzec strategii w wewnętrznej implementacji mechanizmu odśmiecania pamięci, oferując do wyboru kilka algorytmów różniących się właściwościami. Sam programista wybiera strategię odśmiecania najlepiej dopasowaną do profilu jego aplikacji.