Strategia (wzorzec projektowy)

Z Wikipedii, wolnej encyklopedii

Strategiaczynnościowy wzorzec projektowy, który definiuje rodzinę wymiennych algorytmów i kapsułkuje je w postaci klas. Umożliwia wymienne stosowanie każdego z nich w trakcie działania aplikacji niezależnie od korzystających z nich użytkowników.

Problem[edytuj | edytuj kod]

Rozważmy program generujący statystyki dotyczące podanego na wejściu kodu źródłowego takie, jak liczba wierszy, liczba klas itd. W mechanizmie generowania statystyk możemy wyróżnić część kodu specyficzną dla języka programowania, w którym napisany został kod oraz ogólną, niezależną od języka. Chcielibyśmy, aby nasz program był uniwersalny i obsługiwał wiele języków programowania, a także by w przyszłości istniała możliwość dodawania nowych.

Rozwiązaniem jest zastosowanie wzorca Strategia, gdzie algorytmy specyficzne dla języków programowania wydzielimy jako osobne klasy ze wspólnym interfejsem, który umożliwi programowi wyciąganie określonych informacji o kodzie źródłowym.

Budowa[edytuj | edytuj kod]

Diagram klas wzorca Strategia

We wzorcu Strategia definiujemy wspólny interfejs dla wszystkich obsługiwanych algorytmów i zawierający wszystkie dozwolone operacje. Następnie implementujemy go w poszczególnych klasach dostarczających konkretne algorytmy. Dodatkowo, we wzorcu wyróżniamy także klienta korzystającego z algorytmów. Posiada on referencję do aktualnie używanej strategii oraz metodę ustawStrategie(), która pozwala ją zmienić.

Elementy wzorca:

  • Strategia – interfejs definiujący operacje, które muszą obsługiwać wszystkie dostępne algorytmy. Zakładamy, że wszyscy klienci zainteresowani wykorzystaniem algorytmów będą używać właśnie tego interfejsu.
  • Konkretna strategia – implementuje określony algorytm zgodnie ze zdefiniowanym interfejsem.
  • Klient – użytkownik rodziny algorytmów posiadający referencję do obiektu Strategia.

Istotne jest, że obiekty Klient oraz Strategia współpracują ze sobą w celu wykonania określonego zadania. Klient wykonuje wszystkie ogólne zadania i nadzoruje przepływ sterowania, zaś strategie implementują te części zadania, które można wymieniać.

Konsekwencje użycia[edytuj | edytuj kod]

Zalety:

  • wzorzec pozwala na ścisłe, formalne zdefiniowanie rozszerzalnych rodzin algorytmów dzięki wprowadzeniu interfejsu Strategia,
  • bazuje na koncepcji kompozycji, a nie na dziedziczeniu — nie ma sztywnego powiązania między algorytmem a miejscem jego wykorzystania. Może on być wymieniany w trakcie działania programu,
  • eliminacja instrukcji warunkowych,
  • umożliwia wybór implementacji — algorytmy mogą rozwiązywać ten sam problem, lecz różnić się uzyskiwanymi korzyściami (zużycie pamięci, złożoność obliczeniowa, optymalizacja pod kątem pewnych szczególnych przypadków).
  • możliwość niezależnego testowania klientów i strategii[1]

Wady:

  • dodatkowy koszt komunikacji między klientem a strategią (wywołania metod, przekazywanie danych),
  • zwiększenie liczby obiektów.

Implementacja[edytuj | edytuj kod]

Klient i strategia współpracują ze sobą, dlatego musi zachodzić między nimi obustronna wymiana informacji. Klient może udostępniać interfejs umożliwiający strategii pobranie niezbędnych danych, lecz powstaje wtedy jawna zależność zmniejszająca zakres zastosowań strategii oraz utrudniające testowanie. Alternatywne rozwiązanie polega na wstrzykiwaniu danych do strategii w parametrach, co jednak może powodować, że niektóre strategie będą otrzymywać dane, które nie są im potrzebne.

Klient nadzoruje przepływ sterowania, co przeważnie prowadzi do nałożenia na strategię jakichś wymagań związanych z tym, co określone metody mają robić i kiedy. Większość języków programowania udostępnia jedynie najbardziej podstawowe techniki weryfikacji kontraktów (sprawdzanie typów argumentów i wyników). Dlatego możliwe jest stworzenie strategii, która nie będzie poprawnie współpracować z klientem lub przestanie działać po zmianie jego implementacji z powodu naruszenia niektórych założeń. Wzorzec ten nie zapewnia żadnej ochrony przed takimi sytuacjami.

Aby zredukować liczbę obiektów, możemy stworzyć bezstanowe obiekty strategii, co umożliwi wykorzystywanie jednego obiektu przez wiele klientów, również w środowisku współbieżnym. Do ich implementacji możemy wykorzystać wzorzec Pyłek.

Zastosowania[edytuj | edytuj kod]

Oficjalna wirtualna maszyna Javy HotSpot wykorzystuje wzorzec Strategia w wewnętrznej implementacji mechanizmu odśmiecania pamięci, oferując do wyboru kilka algorytmów różniących się właściwościami. Programista wybiera strategię odśmiecania najlepiej dopasowaną do profilu jego aplikacji.

Innym miejscem zastosowania wzorca jest sytuacja, w której poszczególne strategie rozwiązują inny problem. Za ilustrację może posłużyć tutaj sklep internetowy posiadający swoje oddziały w kilku krajach różniących się obowiązującymi w nich przepisami podatkowymi. Klient implementuje podstawową, wspólną dla wszystkich funkcjonalność, zaś operację naliczenia podatku deleguje do strategii zaimplementowanej dla konkretnego kraju.

Przykłady[edytuj | edytuj kod]

Przypisy[edytuj | edytuj kod]

  1. TheStrategyPattern. Net Objectives Pattern Repository Wiki. [dostęp 2011-09-12]. [zarchiwizowane z tego adresu (2011-10-03)]. (ang.).

Bibliografia[edytuj | edytuj kod]