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.

Powiązane tematy

Print Friendly, PDF & Email