Dzisiaj kilka słów na temat wzorców. Póki co krótko. Poszczególne wzorce będą rozszerzane o przykłady kodu. Chciałbym nadmienić, że jest to temat wielokrotnie już opracowywany, rozwijany od kilkudziesięciu lat, ale prezentuję tu moje własne spojrzenie na kwestię wzorców celem przypomnienia. Z doświadczenia wiemy, że pewne rzeczy warto sobie powtórzyć, bo często się przydają…
A zatem zaczynamy! Wzorce można podzielić na trzy główne grupy:

  • Wzorce konstrukcyjne – Ich zadaniem jest tworzenie i inicjacja obiektów, klas, danych.
  • Wzorce strukturalne – Definiują zależności między obiektami, opisują ich strukturę, łączą obiekty w większe struktury.
  • Wzorce czynnościowe – Przedstawiają zależności we współpracy pomiędzy obiektami, ich zależność, zachowanie i odpowiedzialność.

Wzorce konstrukcyjne:

  • Abstract Factory (Fabryka abstrakcyjna) – Sposób tworzenia obiektów jednego typu, gdzie dostarczany jest interfejs, który nie ma zdefiniowanych konkretnych klas.
  • Builder (Budowniczy) – Dostarcza mechanizm, który oddziela tworzenie obiektów od mechanizmu ich użycia.
  • Factory (Fabryka) – Dostarcza mechanizm do tworzenia całych rodzin obiektów, które są spokrewnione albo zależne od siebie, bez konieczności określania rzeczywistego obiektu.
  • Factory Method (Metoda fabrykująca) – Definiuje sposób tworzenia obiektów, gdzie decyzja o rodzajach i sposobie tworzenia obiektów wykorzystywanych klas jest delegowana do klas potomnych.
  • Prototype (Prototyp) – Tworzy obiekt klasy, wykorzystując klonowanie istniejącego obiektu.
  • Singleton (Singleton) – Powoduje, że dana klasa będzie istniała tylko w postaci jednej instancji, oraz zapewnia globalny punkt dostępu do niej. Niekiedy Singleton jest nazywany antywzorcem i można spotkać się z sytuacjami, gdy dostęp do klasy jest realizowany przy pomocy metod statycznych.

Wzorce strukturalne:

  • Adapter (Wrapper) – Jego zadaniem jest konwersja interfejsu danej klasy do postaci interfejsu innej klasy. Adapter pozwala na działanie dwóch klas, które normalnie by ze sobą nie funkcjonowały ze względu na niekompatybilność interfejsu.
  • Facade (Fasada) – Upraszcza oraz unifikuje dostęp do grupy klas lub jednej klasy o rozbudowanym API. Fasada tworzy nowy interfejs o wysokim stopniu abstrakcji, który upraszcza korzystania z całego systemu.
  • Decorator (Dekorator) – Pozwala na dynamiczne przydzielanie danemu obiektowi nowych cech, zachowań. Dekorator oferuje elastyczność jak dziedziczenie, a daje znacznie większą funkcjonalność, co więcej: umożliwia dynamiczne rozszerzanie funkcjonalności w trakcie działania programu.
  • Composite (Kompozyt) – Jego zadaniem jest łączenie obiektów w strukturę drzewa reprezentującą hierarchię części – całość, unifikując dostęp zarówno do kolekcji, jak i pojedynczego obiektu, co umożliwia klientom jednolite traktowanie pojedynczych obiektów i ich kompozycji.
  • Proxy (Pomocnik) – Przesłania z zewnątrz inny obiekt i kontroluje dostęp do niego, może mieć dodatkowe funkcje, np. buforowanie danych, serializację itd.

Wzorce czynnościowe:

  • Strategy (Strategia) – Wzorzec definiujący grupę algorytmów, które są określone jako osobne klasy, co pozwala na ich wymienność. Przeniesienie pewnej implementacji funkcjonalności do osobnych klas pozwala na zmianę tej funkcjonalności bez wpływu na główny wątek aplikacji oraz dynamiczną zmianę w trakcie działania aplikacji.
  • Observer (Obserwator) – Określa relacje pomiędzy obiektami w systemie w stosunku jeden do wielu. Definicję tworzy się w taki sposób, że obiekt obserwowany informuje o zmianie swojego stanu obiekty, które go obserwują. Obiekty obserwujące zostają powiadomione i aktualizują swój stan.
  • Template Method (Metoda szablonowa) – W tym wzorcu projektowym szkielet danego algorytmu jest zawarty w głównej klasie, ale do klas podrzędnych przekazywana jest realizacja pewnych jego kroków. Pozwala to klasom podrzędnym na zmianę wybranych kroków algorytmu, ale jednocześnie uniemożliwia zmianę jego głównej struktury.
  • Iterator (Iterator) – Zapewnia sekwencyjny dostęp do elementów obiektu bez ujawniania jego implementacji wewnętrznej. Zapewnia to możliwość przeglądania kolekcji obiektu bez wiedzy o jego działniu. Niekiedy stosowane są rozbudowane wersje iteratorów, np. iteartor o dostępie dwukierunkowym, swobodnym dostępie itd.
  • Command (Polecenie) – Żądanie wykonania pewnego działania w postaci obiektów, które sposób funkcjonowania hermetyzują w środku. Obiekty te mogą być parametryzowane zróżnicowanymi żądaniami, umieszczone w kolejkach lub rejestracji, a ponadto obsługiwać wycofanie danej operacji.
  • State (Stan) – Wzorzec, który określa obiekt zmieniający swoje zachowanie wedle wewnętrznego stanu. Uzyskujemy w ten sposób złudzenie zmiany klasy tego obiektu.
  • Mediator – Wzorzec, który określa obiekt który jako jedyny zna wszystkie współpracujące ze sobą obiekty, zapewnia komunikację pomiędzy nimi.