Damian Nowak is a CEO at Virtkick. He's a Ruby coder, an Arch Linux hacker, and drinks good beer.

Wzorce projektowe – krótki opis najważniejszychJuly 2010

Krótki wpis z opisem najważniejszych wzorców projektowych. Po więcej szczegółów odsyłam do innych miejsc w sieci. :) Myślę jednak, że taka minimalistyczna lista wzorców może być przydatna. Opracowałem po przeczytaniu książek Refactoring to Patterns Joshuy Kerievskiego i Effective Java Joshuy Blocha.

Adapter

Unifikuje interfejsy różnych klas o tym samym lub bardzo zbliżonym zakresie odpowiedzialności w celu zakrycia różnic między nimi.

Builder

Upraszcza budowanie drzewa obiektów-kompozytów. Budowanie kompozytów nie jest najprzyjemniejsze – najczęściej to seria tworzenia nowych obiektów i umieszczania ich w innych. Builder ukrywa budowanie kompozytu za przyjemnym interfejsem. Drugim zastosowaniem wzorca budowniczego jest budowanie niezmiennych (immutable) obiektów, eliminując niebezpieczne dla wątków settery. Typowa implementacja budowniczego to statyczna wewnętrzna klasa. Przykład:

Cipher cipher =  new Cipher.Builder(Engine.RIJNDAEL).key(256).block(128).mode(Mode.CBC).build();

Chain Constructors

Połączenie wywołań konstruktorów w łańcuch, gdy w konstruktorach powtarzają się takie same fragmenty kodu. Polega to najczęściej na stworzeniu konstruktora zbierającego, który jest wywoływany przez inne konstruktory.

Collecting Parameter

Wzorzec ten warto zastosować, gdy metoda zbierająca dane jest długa. Wprowadza się wtedy obiekt zbierający dane (np. `StringBuilder albo kolekcję) i przekazuje się go po kolei do wielu mniejszych metod – patrz wzorzec Composed Method.

Command

Ukrycie logiki obsługi poleceń w obiektach poleceń. Interfejs takich obiektów jest bardzo prosty, występuje najczęściej tylko jedna metoda, typowo void execute() lub void run(). Wykorzystywane w celu eliminacji długich instrukcji warunkowych w tzw. dispatcherach. Przykładem wzorca Command jest interfejs Runnable. Idealny do implementacji operacji cofnij-ponów.

Composed Method

Przekształcenie dużej, niezrozumiałej metody na kilka małych, prywatnych metod o takim samym poziomie szczegółowości. Duża metoda wywołuje po kolei te mniejsze.

Composite

Drzewo obiektów, na którym można operować jak gdyby operowało się na pojedynczym obiekcie. Przykładem kompozytu jest struktura elementów pliku XML lub klauzula WHERE dla bazy danych zapisana w systemie obiektowym.

Creation Method

Zamiana konstruktorów z różnymi parametrami na statyczne metody o przyjemnych nazwach, które zwracają nowy obiekt. Daje to przy okazji możliwość zmiany implementacji w przyszłości, np. zwracanie obiektów z puli obiektów.

Decorator

Dodanie dodatkowej funkcjonalności do klasy bez jej modyfikacji. Klasa rozszerzana o nową funkcjonalność nie jest świadoma tego, podobnie i użytkownicy tej klasy. Wzorzec jest zgodny z zasadą jednej odpowiedzialności (single responsibility principle), zidentyfikowanej przez Boba Martina. Przykład: składanie strumieni.

Facade

Ukrycie brzydkiego lub nadmiernie rozbudowanego podsystemu za interfejsem uporządkowanym i/lub uproszczonym. Uwaga – często mylony z adapterem.

Factory

Sposób na wyeliminowanie logiki tworzenia obiektów rozrzuconej pomiędzy dwie klasy.

Interpreter

Zapisanie prostego języka w postaci obiektów-kompozytów. Idealny dla prostych zapytań do bazy danych, gdy nie ma z góry zdefiniowanych, stałych zapytań. Przykład:

new AndSpec(new ColorSpec(Color.RED), new OrSpec(new SizeSpec(Size.BIG), new ColorSpec(Color.BLACK)))

Null Object

Usunięcie często powtarzającej się logiki związanej z obsługą wartości null poprzez wprowadzenie klasy, która “nic” nie robi.

Observer

Powiadamianie obserwatorów o zajściu zdarzenia. Ma na celu wyeliminowanie niechcianych zależności pomiędzy klasą powiadamiającą i powiadamianą.

Singleton

Zapewnienie istnienia tylko jednego obiektu danej klasy, gdy tworzenie wielu egzemplarzy byłoby nieefektywne czasowo i pamięciowo. Uwaga – nigdy nie optymalizuj swoich programów na początku, bo ostatecznie dostaniesz kod nie tylko nieoptymalny, ale również trudny w utrzymaniu. Zajrzyj do Effective Java, aby uzyskać więcej informacji na ten temat.

State

Wykorzystanie prostych obiektów stanu zamiast wyrażeń warunkowych w celu uproszczenia logiki zmian stanu oraz poprawie czytelności.

Strategy

Hermetyzacja pojedynczego algorytmu przetwarzania w obiekcie strategii zamiast definiowania wszystkich możliwych sposobów przetwarzania w jednym miejscu i wybieraniu odpowiedniego za pomocą długich instrukcji warunkowych. Zawsze przed zastosowaniem dziedziczenia warto pomyśleć, czy wzorzec strategii nie będzie lepszym rozwiązaniem.

Template Method

Umieszczenie części wspólnych kilku klas w abstrakcyjnej klasie bazowej i pozostawienie części rozłącznych w klasach ją rozszerzających. Uwaga - jednym z niebezpieczeństw tego wzorca jest problem ułomnej klasy bazowej. Zajrzyj do książki Holub on patterns lub innych publikacji Allena Holuba.

Damian Nowak
CEO & Ruby Developer