niedziela, 23 marca 2014

Wizytator

Wizytator (ang. Visitor)

Kolejny wzorzec to wizytator lub inaczej nazywany odwiedzającym. Jego zadanie jak sama nazwa wskazuje jest odwiedzanie innych obiektów i wykonanie na nim konkretnych działań. Różne implementacje wizytatorów, mogą wykonywać różne zadania, rozszerzając funkcjonalność struktury elementów, bez ich wewnętrznej modyfikacji. Czasem będzie potrzeba zadeklarowania przyjaźni friend (c++) choć to zależy do czego będziemy wykorzystywali nasz wizytator.

Podstawowymi metodami we wzorcu są metody visit() oraz accept().
W jednym z moich ostatnich postów przedstawiałem wzorzec kompozyt, którego przykład opierał się na bardzo popularnym schemacie. A mianowicie Shape, Rectangle, Square, Ellipse. Mamy jakąś klasę bazową (abstrakcyjną) Shape, a następnie po niej dziedziczą inne klasy typu Rectangle, Square itp. Tak więc w omawianym wzorcu wrócimy do tego przykładu i rozszerzymy go o nową funkcjonalność. Do klasy Shape dodamy metodę
accepr(Visitor &p_visitor) = 0;
Każda dziedzicząca po niej klasa będzie musiała rzecz jasna implementować powyższą metodę (no chyba, że dalej będzie chciała pozostać klasą czysto wirtualną, co w tym przypadku nie będzie sensowne). Następnie w samej metodzie accept() wywołuje się metodę visit() na rzecz wizytatora przyjętego przez referencje do metody accept().

Przykładowo:
void Rectangle::accept(Visitor &p_visitor)
{
 p_visitor.visit(*this);
}
Taka metoda accept() jest w każdej klasie typu Square, Rectangle, Ellipse itp. Oprócz tego musimy jeszcze stworzyć klasę wizytator, w którym trzeba będzie zaimplementować metody visit(...), które jako argument będą przyjmowały odpowiednie klasy typu Rectangle, Ellipse, Square przez referencję w przypadku języka c++. W metodzie accept() w konkretnej klasie kształtu wywołamy metodę visit() z argumentem *this, czyli na samego siebie, ponieważ nasz wizytator właśnie nas będzie chciał wizytować.
virtual void visit(const Rectangle &p_rectangle);
virtual void visit(const Ellipse &p_ellipse);
virtual void visit(const ComplexShape &p_complexShape);
Przykładowa implementacja metody visit() dla klasy Rectangle
void PrinterVisitor::visit(const Rectangle &p_rectangle)
{
 std::cout << " Rectangle : " << std::endl;
 std::cout << "p_rectangle.xy.first = " << p_rectangle.xy.first << std::endl;
 std::cout << "p_rectangle.xy.second = " << p_rectangle.xy.second << std::endl;
}
Na początku wydawać się to może lekko zagmatwane więc najlepiej spojrzeć na przykład. Poniżej zamieszczę najważniejsze fragmenty kodu, na które na pewno trzeba spojrzeć żeby zrozumieć zasadę działania wzorca. Przykład będzie niemal, że identyczny do tego ze wzorca kompozyt rozbudowany teraz będzie o wzorzec wizytator.

Przykład
class Visitor
{
public:
 Visitor();
 virtual ~Visitor();
 
 virtual void visit(const Rectangle &p_rectangle) = 0;
 virtual void visit(const Ellipse &p_ellipse) = 0;
 virtual void visit(const ComplexShape &p_complexShape) = 0;
};

class PrinterVisitor : public Visitor
{
public:
 PrinterVisitor();
 virtual ~PrinterVisitor();

 virtual void visit(const Rectangle &p_rectangle);
 virtual void visit(const Ellipse &p_ellipse);
 virtual void visit(const ComplexShape &p_complexShape);
};
class Shape
{
public:
 std::pair translatedToXY;
 float rotatedByAlpha;
 std::pair rotatedAroundXY;

public:
 Shape(std::pair);
 Shape(void);
 virtual ~Shape(void);

 virtual bool in(std::pair p_pair);
 virtual void translate(std::pair p_translate);
 virtual void rotate(std::pair p_rotationAroundXY, float p_rotationAlpha);

 std::pair getTranslatedToXY(void);
 void setTranslatedToXY(std::pair p_translatedToXY);
 std::pair getRotatedAroundXY();
 void setRotatedAroundXY(std::pair p_rotatedAroundXY);
 float getRotatedByAlpha(void);
 void setRotatedByAlpha(float p_rotatedByAlpha);

 friend class Utils;

 virtual void accept(Visitor &p_visitor) = 0;
};
class Rectangle : public Shape
{
public:
 std::pair xy;
 std::pair xy2;

public:
 Rectangle(std::pair p_xy, std::pair p_xy2);
 Rectangle(void);
 virtual ~Rectangle(void);

 bool in(std::pair p_point);
 std::pair getXy(void);
 void setXy(std::pair p_xy);
 std::pair getXy2(void);
 void setXy2(std::pair p_xy2);

 // friend class Utils;
 virtual void accept(Visitor &p_visitor);
};
Warto zwrócić szczególną uwagę na klasę complexShape, a mianowicie na jej metodę accept(). Jest ona rzecz jasne nieco inna niż analogiczne metody z klas Rectangle czy Ellipse.
void ComplexShape::accept(Visitor &p_visitor)
{
 shape1->accept(p_visitor);
 shape2->accept(p_visitor);
 p_visitor.visit(*this);
}
Powyżej wywołaliśmy metodę accept() na rzecz obu wskaźników na kształty jakie przechowuje klasa ComplexShape, a dopiero potem metodę visit() z argumentem *this.

Poniżej zamieszczam linka do pełnego kodu z przykładem skompilowanym pod środowiskiem Visual Studio 2013. LINK Z KODEM

Na koniec diagram klas dla wzorca wizytator

Kompozyt (obiektowy)

Kompozyt (ang. Composite)
Tym razem na warsztat bierzemy wzorzec kompozyt.
Celem wzorca jest składanie obiektów w taki sposób, aby klient widział wiele z nich jako pojedynczy obiekt. Wzorzec ten stosuje się, gdy wygodniej jest korzystać z pewnych operacji dla danego obiektu w ten sam sposób jak dla grupy obiektów, np. rysując na ekranie prymitywy lub obiekty złożone z prymitywów. Zmieniając rozmiar zarówno pojedynczych prymitywów jak i obiektów złożonych z prymitywów (z zachowaniem proporcji).
Najłatwiej przedstawić wzorzec na przykładzie klasy Shape, która jest klasą abstrakcyjną. Po niej dziedziczą klasy przykładowo Rectangle, Square, Ellipse. Jest to dość standardowy przykład. Jednak tworzymy dodatkowo klasę, która będzie się nazywała complexShape. Klasa ta będzie zawierała wskaźniki na dwa Shape-y, które też de facto będą mogły być ComplexShape-ami. I tak, aż do ostatniego shape-a, który będzie prymitywem. Mając klasę complexShape, a w niej wcześniej wspomniane dwa wskaźniki na shape-y, możemy działać na obiekcie complexShape, tak jakby był to typ prosty. Przykładowo wywyołując operacje move() (przesuń), rotate() (obróć), obracamy lub przesuwamy cały obiekt (dwa shape-y), ponieważ complexShape jest tak zakodowany, że wywołuje metody te na swoich wskaźnikach. Czyli ostatecznie przesuwamy bądź obracamy dwa kształty.
Czas na przykład w c++, o którym mówiliśmy. Poniżej umieszczę tylko najważniejsze fragmenty kodu, ponieważ jest on dość długi więc całość będzie dostępna pod linkiem, który zamieszczę pod koniec.
class Shape
{
public:
 std::pair translatedToXY;
 float rotatedByAlpha;
 std::pair rotatedAroundXY;

public:
 Shape(std::pair);
 Shape(void);
 virtual ~Shape(void);

 virtual bool in(std::pair p_pair);
 virtual void translate(std::pair p_translate);
 virtual void rotate(std::pair p_rotationAroundXY, float p_rotationAlpha);

 std::pair getTranslatedToXY(void);
 void setTranslatedToXY(std::pair p_translatedToXY);
 std::pair getRotatedAroundXY();
 void setRotatedAroundXY(std::pair p_rotatedAroundXY);
 float getRotatedByAlpha(void);
 void setRotatedByAlpha(float p_rotatedByAlpha);

 friend class Utils;
};
class Rectangle : public Shape
{
public:
 std::pair xy;
 std::pair xy2;

public:
 Rectangle(std::pair p_xy, std::pair p_xy2);
 Rectangle(void);
 virtual ~Rectangle(void);

 bool in(std::pair p_point);
 std::pair getXy(void);
 void setXy(std::pair p_xy);
 std::pair getXy2(void);
 void setXy2(std::pair p_xy2);

 // friend class Utils;
};
class Ellipse : public Shape
{
public:
 std::pair xy;
 float rx, ry;

public:
 Ellipse(std::pair p_xy, float p_rx, float p_ry);
 Ellipse(void);
 virtual ~Ellipse(void);

 virtual bool in(std::pair p_point);
 std::pair getXy(void);
 void setXy(std::pair p_xy);
 float getRx(void);
 void setRx(float p_rx);
 float getRy(void);
 void setRy(float p_ry);
};
class ComplexShape : public Shape
{
public:
 Shape* shape1;
 Shape* shape2;

 enum Operator {
  Intersection, Union, Difference
 };
 Operator operator_;

 ComplexShape(Shape* shape1, Shape* shape2, Operator operator_);
 ComplexShape(void);
 virtual ~ComplexShape(void);

 Operator getOperator(void);
 void setOperator(Operator operator_);
 virtual bool in(std::pair p);
 Shape* getShape1(void);
 void setShape1(Shape* shape1);
 Shape* getShape2(void);
 void setShape2(Shape* shape2);
 void translate(std::pair xy);
};
Przykładowa metoda translate(), w której widać, że klasa complexShape wywołuje metody translate() na swoich wskaźnikach. Dzięki temu wywołując metode translate() na complexShape-ie przesuwamu cały złożony obiekt.
void ComplexShape::translate(std::pair xy) 
{
 shape1->translate(xy);
 shape2->translate(xy);
}
Link do całego kodu - projekt został skompilowany w środowisku Visual Studio 2013:
LINK
W razie jakiś pytań problemów proszę pisać w komentarzach bądź wysłać emaila. Diagram klas dla wzorca kompozyt.

poniedziałek, 3 marca 2014

Adapter (klasowy oraz obiektowy)

Adapter (Wrapper)

Problem:

Mamy dwie klasy o niekompatybilnych interfejsach i chcemy przekształcić jeden interfejs w drugi (inny), bądź nowy.

Zadania: 
  • celem jest umożliwienie współpracy dwóm klasom o niekompatybilnych interfejsach. 
  • przekształca interfejs jednej z klas na interfejs drugiej klasy 
  • innym zadaniem adaptera jest opakowanie istniejącego interfejsu w nowy.
Przykład: 

Jako przykład chciałbym przedstawić następujący kod. Mamy klasę Stack oraz List. Zamierzenie jest takie, aby za pomocą metod z klasy Stack wywoływać metody z klasy List, aby stworzyć prosty adapter.. W tym celu należy stworzyć nową klasę ListImpStack, która będzie dziedziczyć po List oraz implementować Stack. Następnie w definicjach metod klasy Stack wywoływane są metody z klasy List.
interface Stack
{
    void push (T o) { ... }
    T pop () { ... }
    T top () { ... }
} 


/*klasa adaptowana*/
class List
{
    public void insert (DNode pos, T o) { ... }
    public void remove (DNode pos) { ... }
 
    public void insertHead (T o) { ... }
    public void insertTail (T o) { ... }
 
    public T removeHead () { ... }
    public T removeTail () { ... }
 
    public T getHead () { ... }
    public T getTail () { ... }
} 


/*Klasa ListImpStack adaptuje klasę List opakowując ją w interfejs klasy Stack*/
class ListImpStack extends List implements Stack
{
    public void push (T o) {
        insertTail (o);
    } 
    public T pop () {
        return removeTail ();
    } 
    public T top () {
        return getTail ();
    }
}

Singleton (obiektowy)

Singleton (Singleton) 

Jego celem jest ograniczenie możliwości tworzenia obiektów danej klasy do jednej instancji oraz zapewnienie globalnego dostępu do stworzonego obiektu. Innymi słowy tylko jeden obiekt danej klasy może zostać utworzony.

W celu napisania wzorca projektowego, jakim jest Singleton należy (odnosi się do języków obiektowych takich, jak np.: C++):

  1. Konstruktor domyślny, konstruktor kopiujący zadeklarować w sekcji private. 
  2. Jeśli chodzi o standard C++11 to w sekcji private należy jeszcze zadeklarować konstruktor przenoszenia oraz operator przenoszenia.
  3. Operator = kopiujący zadeklarować w sekcji private
  4. Stworzyć statyczny wskaźnik, referencję w sekcji private, która będzie jedynym "obiektem" tej klasy.
  5. W sekcji public zadeklarować i zdefiniować metodę o nazwie np. getInstance, która będzie zwracała przez wskaźnik, bądź referencję pole instancji klasy, czyli to, co zadeklarowaliśmy w punkcie 3.
  6. W zależności od języka można jeszcze zadeklarować i zdefiniować metodę o nazwie, np.: deteteInstance w sekcji public, która będzie w razie potrzeby dealokować przydzieloną pamięć wskaznikowi, który utworzyliśmy w punkcie 3.

Przykładowy kod napisany w języku C++11 został przedstawiony poniżej:

Plik Singleton.h:
//Singleton.h
#ifndef SINGLETON_H
#define SINGLETON_H

#include 

class Singleton
{
  static Singleton *instance;
  
  Singleton(void);
  Singleton(const Singleton &pattern) = delete;
  Singleton(Singleton &&pattern) = delete;
  Singleton& operator=(const Singleton &pattern) = delete;
  Singleton& operator=(Singleton &&pattern) = delete;
  
public:
  virtual ~Singleton(void);
  
  static Singleton& getInstance(void);
  static void deteteInstance(void);
  
  void print(void);
};

#endif
Plik Singleton.cpp:
//Singleton.cpp
#include "Singleton.h"

Singleton* Singleton::instance = nullptr;

Singleton::Singleton()
{  
}

Singleton::~Singleton()
{  
}

Singleton& Singleton::getInstance()
{
  if(instance)
  {
    instance = new Singleton();    
  }
  return *instance;
}

void Singleton::deteteInstance()
{  
  if(instance)
  {
    delete instance;
    instance = nullptr;
  }
}

void Singleton::print()
{
  std::cout << "Hello" << std::endl;
}
Plik main.cpp:
//main.cpp
#include 
#include "Singleton.h"

int main(int argc, char **argv)
{
  Singleton &single = Singleton::getInstance();
  single.print();
  return 0;
}
Plik makefile potrzeby do odpalenia programu na Linuxie:
#makefile
all:
 g++ -std=c++0x Singleton.cpp main.cpp -o main && ./main
clean:
 rm -f *.cpp~ && rm -f *~ && rm -f main

Cały kod znajduję się pod tym linkiem:

sciagnij_Singleton

Poniżej przedstawiony został diagram klas:

niedziela, 2 marca 2014

Polecenie (obiektowy)

Polecenie (Command)


Traktujący żądanie wykonania określonej czynności jako obiekt, dzięki czemu mogą być one parametryzowane w zależności od rodzaju odbiorcy, a także umieszczane w kolejkach i dziennikach.

Zastosowanie:
- Wzorzec znajduje zastosowanie wszędzie tam, gdzie musimy zapamiętywać wykonywane operacje lub je wycofywać.

- Gdy identyczne polecenia muszą być parametryzowane różnymi danymi w zależności od tego, kto odpowiada za ich obsłużenie.

Przykład:
import java.util.*;

public class CommandQueue {
1. Implementujemu interfejst Command - nasze polecenie, a w nim metode execute().
interface Command { void execute(); }
2. Każdy z zawodów implementuje interfejs oraz metoda exeecute() na swój sposób, ponieważ każdy zawód wykonuje swoją odrębną część.
static class DomesticEngineer implements Command {
   public void execute() {
      System.out.println( "take out the trash" );
   }  
}
3. Zadanie dla policjanta określone w metodzie execute().
static class Politician implements Command {
   public void execute() {
      System.out.println( "take money from the rich, take votes from the poor" );
   }  
}
4. Zadanie dla programisty określone w metodzie execute().
static class Programmer implements Command {
   public void execute() {
      System.out.println( "sell the bugs, charge extra for the fixes" );
   }  
}
5. Lista żadań, zadań.
public static List produceRequests() {
   List queue = new ArrayList();
   queue.add( new DomesticEngineer() );
   queue.add( new Politician() );
   queue.add( new Programmer() );
   return queue;
}
6. Metoda wykonująca wszystkie metody execute().
public static void workOffRequests( List queue ) {
   for (Iterator it = queue.iterator(); it.hasNext(); )
      ((Command)it.next()).execute();
}
7. Main programu.
public static void main( String[] args ) {
   List queue = produceRequests();
   workOffRequests( queue );
}
}
 
Diagram:

Odwiedzający (obiektowy)

Odwiedzający (Visitor) 

Zadaniem jest "odwiedzenie" każdego elementu w danej strukturze obiektów i wykonanie na nim konkretnych działań. Różne implementacje wizytatorów, mogą wykonywać różne zadania, rozszerzając funkcjonalność struktury elementów, bez ich wewnętrznej modyfikacji. 

Przykład 
Program będzie dotyczył tworzenia kształtów takich jak kwadrat, prostoskąt, elispa i kształt złożony mogący składać się z dwóch kształtów. Zadaniem wizytatora będzie odwiedzenie każdego kształtu, a następnie wyświetlenie o nim danych, np. w przypadku elipsy jej dwóch promieniów. Wszystkie kształty będą dziedziczyły po klasie Shape (kształt). 

W poniższym przykładzie przedstawie tylko najważniejsze fragmenty kodu. 

1. Na początek tworzymy klase Shape. Najważaniejsza w tej klasie dla naszych potrzeb jest metoda accept(). Przyjmuje ona wizytatora. Narazie jest czysto wirtualna. Implementować ją będziemy dopiero w klasach dziedziczących po Shape.
class Shape
{
 // ...
public:
 virtual void accept(Visitor &p_visitor) = 0;
 // ...
};
2. Pierwsza klasa dziedzicząca po Shape. Mamy tutaj dwa pola określające położenie prostokąta oraz metode accept(), którą teraz trzeba będzie zaimplementować.
class Rectangle : public Shape
{
 // ...
public:
 std::pair xy;
 std::pair xy2;

 virtual void accept(Visitor &p_visitor);
};
3. Klasa Rectangle.cpp. Najważniejsze fragmenty. Metoda accept() jak już wcześniej zostało wspomniane przyjmuje wizytatora. Wizytator jak się już nie długo przekonamy będzie implementował metodę visit(), do której przyjmować będzie konkretny rodzaj kształtu - shape-a. Dlatego więc do wizyt przekazujemy poniżej *this bo chcemy przekazać obiekt (samego siebie) rectangl-a do metody visit wizytatora, żeby ten mógł np. wyświetlić dane prostokąta.
void Rectangle::accept(Visitor &p_visitor)
{
 p_visitor.visit(*this);
}
4. Analogicznie sprawa wygląda dla klasy Ellipse z tą różnicą, że elipsa posiada dwa promienie oraz swoje położenie.
class Ellipse : public Shape
{
 // ...
public:
 std::pair xy;
 float rx, ry;
 
 virtual void accept(Visitor &p_visitor);
};
5. Na podobnej zasadzie, jak w prostokącie zaimplementowana została metoda accept() w elipsie. Prosze spojrzeć.
void Ellipse::accept(Visitor &p_visitor)
{
 p_visitor.visit(*this);
}
6. Klasa complexShape jest nieco bardziej złożona, ponieważ składa się conajmniej z dwóch kształtów. Nawet niekoniecznie podstawowych, ponieważ jeden z podkształtów albo nawet dwa mogą być również kształtami złożonymi. Z tego więc powodu klasa ComplexShape przechowuje dwa wskaźniki na Shape-y oraz standardowo metode accept().
class ComplexShape : public Shape
{
 // ...
public:
 Shape* shape1;
 Shape* shape2;

 virtual void accept(Visitor &p_visitor);
};
7. Implementacja metody accept jest nieco inna od np. implementacji w klasie Rectangle. W klasie ComplexShape przechowujemy dwa kształty, które mogą być jeszcze złożone więc należy każdy z tych kształtów odwiedzić. Trzeba najpierw wywołać metody accept() na wewnętrznych kształtach, a następnie wywołąć metodę visit() na wizytatorze przekazanym przez argument funkcji. Będzie tutaj działała rekurencja.
void ComplexShape::accept(Visitor &p_visitor)
{
 shape1->accept(p_visitor);
 shape2->accept(p_visitor);
 p_visitor.visit(*this);
}
8. Teraz czas na klase wizytatora. Najważniejsze jest to, że przechowuje on trzy metody visit. Każda z nich różni się od reszty tym, iż przyjmuje różne kształty jako argumenty, a właściewie to wszystkie, które zaimplementowaliśmy wyżej. To dzięki temu zabiegowi jeżeli w powyższych metodach accept(), do których przekażemy wizytatora, a następnie wywołamy visit(*this). Właśnie dzięki temu, że zdefiniowaliśmy trzy metody visit(), przyjmujące różne kształy nasza wizytator będzie wiedział, który kształt został przyjęty i odpowiednio do tego się zachowa. Narazie tylko interfejs wizytatora. Konkretna specjalizacja wizytatora będzie poniżej.
class Visitor
{
public:
 Visitor();
 virtual ~Visitor();
 
 virtual void visit(const Rectangle &p_rectangle) = 0;
 virtual void visit(const Ellipse &p_ellipse) = 0;
 virtual void visit(const ComplexShape &p_complexShape) = 0;
};
9. Konkretna spoecjalizacja wizytatora. Będzie to wizytator drukujący.
class PrinterVisitor : public Visitor
{
public:
 PrinterVisitor();
 virtual ~PrinterVisitor();

 virtual void visit(const Rectangle &p_rectangle);
 virtual void visit(const Ellipse &p_ellipse);
 virtual void visit(const ComplexShape &p_complexShape);
};
10. Implementacja metod. Każda na swój sposób, ponieważ każdy z kształtów przechowuje inne dane.
void PrinterVisitor::visit(const Rectangle &p_rectangle)
{
 std::cout << " Rectangle : " << std::endl;
 std::cout << "p_rectangle.xy.first = " << p_rectangle.xy.first << std::endl;
 std::cout << "p_rectangle.xy.second = " << p_rectangle.xy.second << std::endl;
}

void PrinterVisitor::visit(const Ellipse &p_ellipse)
{
 std::cout << " Ellipse : " << std::endl;
 std::cout << "p_ellipse.xy.first = " << p_ellipse.xy.first << std::endl;
 std::cout << "p_ellipse.xy.second = " << p_ellipse.xy.second << std::endl;
}

void PrinterVisitor::visit(const ComplexShape &p_complexShape)
{
 std::cout << " ComplexShape : " << std::endl;
 std::cout << "p_complexShape.shape1->rotatedByAlpha = " << p_complexShape.shape1->rotatedByAlpha << std::endl;
 std::cout << "p_complexShape.shape2->rotatedByAlpha = " << p_complexShape.shape2->rotatedByAlpha << std::endl;
}
11. Na koniec main programu - użycie wizytatora.
 PrinterVisitor printerV;
 rectangle.accept(printerV);
 elipse1.accept(printerV);

Diagram klas: 




Łańcuch zobowiązań (obiektowy)

Łańcuch zobowiązań/odpowiedzialności (Chain of responsibility)


Zadanie może być przetwarzane przez różne obiekty w zależności od jego typu.
Umożliwia obsłużenie zadania więcej niż jednemu obiektowi.

Zalety wzorca:
1) elementy łańcucha mogą być dynamicznie dodawane i usuwane w trakcie działania programu
2) zmniejszenie liczby zależności między nadawcą, a odbiorcami
3) implementacja pojedynczej procedury nie musi znać struktury łańcucha oraz innych procedur

Wady:
1) wzorzec nie gwarantuje, że każde żądanie zostanie obsłużone

Powszechnie stosowany w systemach okienkowych do obsługi zdarzeń takich jak kliknięcie myszki,
przyciśnięcie klawisza.

We wzorcu chodzi głównie o to, że jeżeli przychodzi jakieś żadanie do wykonania to istnieje
kilka stanów, które to żadanie mogą obsłużyć. Procedura wygląda tak, że jeżeli żadanie nie
zostaje obsłużone przez pierwszy obiekt to żadanie zostaje przekazane do następnego obiektu
obsługującego zdażenia. Obsługa zdażeń nie jest zapętlona. Jeżeli żądanie nie zostaje obsłużone
przez żaden obiekt to poprostu kończy się procedura i tyle. Jest to jedna z wad,
jaką charakteryzuje ten wzorzec. Została ona przedstawiona kilka zdań wcześniej.

Diagram klas.


Widać jak zadania są przekazywane z jednej klasy obsługującej do drugiej. Powtórze jeszcze raz.
Dzieje się tak, ponieważ jeżeli klasa nie potrafi obsłuzyć zdarzenia przekazuje to zdarzenie dalej
(do następnej klasy), która też próbuje obsłużyć to zdarzenie. Czasem bywa tak, że żadna klasa nie
potrafi obsłużyć zdarzenia.
Startujemy od Clienta, który wysyła żądanie do pierwszej klasy obsługującej. Jak widać wszsytko
dzieje się sekwencyjnie (podobnie jak w iteratorze). Inaczej nie miało by to sensu.

Przykład z mailem
Załóżmy, że mamy stworzyć system, który będzie segregował przychodzące maile. Jeżeli przyjdzie
spam to zostanie przeniesiony do kosza. Jeżeli jakaś pochwałą do zostanie przekazany na skrzynkę
do sefa firmy. Jeżeli skarga to do działu obsługi błędów.
Po odebraniu list przekazywany jest do pierwszej procedury obsługi - tej, która radzi sobie ze spamem.
Jeżeli nie pasuje ona do złożonego spamu, jego przetwarzanie jest przekazywane do procedury obsługi
pochwał. I tak dalej...

Każdy list jest przekazywany do pierwszej procedury->
->Procedura Obsługi Spamu->
->Procedura Obsługi Pochwał->
->Procedura Obsługi Skarg->
->Procedura Obsługi Zapotrzebowań.

Myśle, że zasada działania tego wzorca powinna być już jasna.

sobota, 1 marca 2014

Strategia (obiektowy)

Strategia (Strategy)


Stosujemy wtedy gdy mamy możliwość wyboru kilku algorytmów wykonania różniących się właściwościami. Programista wybiera strategie, która będzie dla niego optymalna do wykonania. Innymi słowy strategia 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 klientów. 

Wzorzec ten jest podobny do wzorca metody szablonowej. Przedstawie różnice jakie występują pomiędzy tymi dwoma wzorcami po przeanalizowaniu pierwszego przykładu. 

Czas na przykład.

1. Na początek zostaje zdefiniowany interfejs strategii wraz z metodą solve(). Oczywiście nazwa metody może być dowolna.
interface Strategy { public void solve(); }
2. W strategii podobnie jak w metodzie szablonowej istnieje jedna metoda solve(), jednak różnica polega na tym, iż w przypadku strategii metodę tą możemy redefiniować. W strategii metody są różne i metoda solve() może być różnie zaimplementowana.
abstract class TemplateMethod1 implements Strategy {
   public void solve() {
      start();
      while (nextTry() && ! isSolution())
         ;
      stop();
   }
   protected abstract void    start();
   protected abstract boolean nextTry();
   protected abstract boolean isSolution();
   protected abstract void    stop();
}
3. Tworzymy teraz konkretną implementacje klasy TemplateMethod1. Z tego wynika, iż należy zdefiniować odpowiednio metody: 
start(); 
nextTry(); 
isSolution(); 
stop();
class Impl1 extends TemplateMethod1 {
   private int state = 1;
   protected void start() {
     System.out.print( "start  " );
   }
   protected void stop() {
     System.out.println( "stop" );
   }
   protected boolean nextTry() {
      System.out.print( "nextTry-" + state++ + "  " );
      return true;
   }
   protected boolean isSolution() {
      System.out.print( "isSolution-" + (state == 3) + "  " );
      return (state == 3);
   }
}
4. Nowa/inna klasa abstrakcyjna, która jest analogiczna do klasy TemplateMethod1. Prosze zwrócić uwagę jakie teraz klasa zawiera metody oraz jak została zaimplementowana metoda solve(). Na tym właśnie polega różnica, co do wzorca metody szablonowej.
abstract class TemplateMethod2 implements Strategy {
   public void solve() {                             
      while (true) {
         preProcess();
         if (search()) break;
         postProcess();
      }
   }
   protected abstract void preProcess();
   protected abstract boolean search();
   protected abstract void postProcess();
}
5. Analogiczna sytuacja do klasy Impl1. Tworzymy teraz konkretną implementacje klasy TemplateMethod2. Należy teraz odpowiednio zdefiniować metody: 
preProcess(); 
search(); 
postProcess();
class Impl2 extends TemplateMethod2 {
   private int state = 1;
   protected void    preProcess()  { System.out.print( "preProcess  " ); }
   protected void    postProcess() { System.out.print( "postProcess  " ); }
   protected boolean search() {
      System.out.print( "search-" + state++ + "  " );
      return state == 3 ? true : false;
   }
}
6. Na zakończenie krótki test naszych klas.
public class StrategyDemo {
   public static void clientCode( Strategy strat ) {
     strat.solve();
   }
   public static void main( String[] args ) {
      Strategy[] algorithms = { new Impl1(), new Impl2() };
      for (int i=0; i < algorithms.length; i++) {
         clientCode( algorithms[i] );
      }
   }
}
Zachecam do przeczytania wpisu o metodzie szablonowej. Dzięki temu będzie można łatwo rozróżnić różnice pomiędzy strategią, a metodą szablonową.

Diagram klas: 


Metoda szablonowa (klasowy)

Metoda szablonowa (Template method)


Metoda szablonowa jest wzorcem bardzo przyjemnym i łatwym w zrozumieniu.
Głównym metody szablonowej zadaniem jest zdefiniowanie metody, będącej szkieletem algorytmu. Algorytm ten może być następnie dokładnie definiowany w klasach pochodnych. Niezmienna część algorytmu zostaje opisana w metodzie szablonowej, której klient nie może nadpisać. W metodzie szablonowej wywoływane są inne metody, reprezentujące zmienne kroki algorytmu. Mogą one być abstrakcyjne lub definiować domyślne zachowania. Klient, który chce skorzystać z algorytmu, może wykorzystać domyślną implementację bądź może utworzyć klasę pochodną i nadpisać metody opisujące zmienne fragmenty algorytmu. Najczęściej metoda szablonowa ma widoczność publiczną, natomiast metody do przesłonięcia mają widoczność chronioną lub prywatną, tak, aby klient nie mógł ich użyć bezpośrednio.
Inna popularna nazwa tego wzorca to niewirtualny interfejs (ang. Non Virtual Interface).

Nazwa wzorca pomimo swojej nazwy nie ma nic wspólnego z szablonami jakich używamy w codziennym programowaniu w językach takich jak np. Cpp lub Java.

Przykład, najpierw bardzo teoretyczny :

Biblioteki, wspomagające automatyzację testów jednostkowych. Biblioteka jUnit wykorzystywana w programowaniu w języku Java definiuje ogólny algorytm wykonywania testów. Składa się on z trzech podstawowych kroków: przygotowania środowiska do wykonania testów, wykonania testów, a następnie posprzątania po wykonanych testach. Kroki te reprezentowane są przez metody setUp, runTest oraz tearDown. Wymienione metody wykonywane są w niezmiennej kolejności przez metodę run, której klient nie może zmienić. Dzięki temu, użytkownik nie może zmieniać kolejności podstawowych bloków algorytmu, może jednak nadpisać metody setUp, runTest oraz tearDown, co pozwala mu dostosować testy do swoich potrzeb. Najprostsza zmianką może być np. dodanie wyświetlenia tekstu jaka aktualnie metoda się wykonuje.

W internecie można znaleść również informacje o tym, że stosowanie tego wzorca często określane jest jako "zasada Hollywood", która mówi "nie dzwoń do nas, my zadzwonimy do Ciebie". Oznacza ona, że każdy z modułów aplikacji nie interesuje się tym skąd przychodzą żądania i dokąd kierowane są odpowiedzi. Wykonuje tylko postawione przed nim zadanie.

Metoda szablonowa jest podobnym wzorcem do wzorca strategii. Zanim jednak napisze na czym polega różnica chciałbym pokazać i wytłumaczyć na czym polega omawiany wzorzec. W rozdziale o strategi przedstawie różnice pomiędzy tymi dwoma wzorcami. Czas na przykład programu :

1. Ponizej pierwsza klasa abstrakcyjna, w której znajduje się metoda findSolution(), której definicja nie będzie już zmieniana w poniższych klasach. Jedynym zmianom implementacyjnym mogą ulec metody :
stepOne();
stepTwo();
stepThr();
stepFor();
Co więcej metody stepTwo() oraz stepThr() musza zostać zaimplementowane, ponieważ narazie są tylko abstrakcyjne.
abstract class Generalization {

   public void findSolution() {
      stepOne();
      stepTwo();
      stepThr();
      stepFor();
   }
   
   protected void stepOne() { System.out.println( "Generalization.stepOne" ); }
   
   abstract protected void stepTwo();
   abstract protected void stepThr();
   protected void stepFor() { System.out.println( "Generalization.stepFor" ); }
}
2. Następnie specjalizacja klasy Generalization. Jednak jak można zauważyć klasa ta pozostaje abtrakcyjna, ze względu na to, iż nie ma zaimplementowanej metody stepTwo(). Prosze zwrócić uwagę również na to, że metoda stepThr() wywołuje w sobie metody : 
step3_1(); 
step3_2(); 
step3_3(); 
Co warte również uwagi to to, że metoda step3_2() nie jest zdefiniowana w tej klasie.
abstract class Specialization extends Generalization {

   protected void stepThr() {
      step3_1();
      step3_2();
      step3_3();
   }
   
   protected void step3_1() { System.out.println( "Specialization.step3_1" ); }
   abstract protected void step3_2();
   protected void step3_3() { System.out.println( "Specialization.step3_3" ); }
}
3. Następna klasa to Realization, która dziedziczy po klasie Specialization, dlatego więc poniższa klasa musi implementować metodę step3_2(). Oprócz tego zmienionych zostało pare metod, które wykonywane są w metodzie findSolution() z klasy Generalization.
class Realization extends Specialization {

   protected void stepTwo() { System.out.println( "Realization   .stepTwo" ); }
   protected void step3_2() { System.out.println( "Realization   .step3_2" ); }

   protected void stepFor() {
      System.out.println( "Realization   .stepFor" );
      super.stepFor();
   }  
}
4. Na sam koniec testujemy naszą klasę wywołując metodę findSolution() na rzecz obiektu algorithm.
class TemplateMethodDemo {
   public static void main( String[] args ) {
      Generalization algorithm = new Realization();
      algorithm.findSolution();
   }  
}
Innym przykładem w teorii mogłoby być tworzenie pizzy. Metoda w klasie abstrakcyjnej nazywała by się twórz pizze i ta metoda nie ulegałaby żadnym zmianom. Byłby to algorytm do przygotowywania pizzy. 1. Stwórz ciasto 2. Dodaj składniki 3. Dodaj sos Każdą pizze tworzy się według powyższego algorytmu, a jedynie, co wyróżnia jedną pizzę od drugiej to jej ciasto, składniki, sos. Tak więc implementowalibyśmy tylko konkretne kroki algorytmu w klasach rzeczywistych. Innym przykładem może być gra. Poniżej umieszczam kod, a przeanalizowanie pozostawiam jako ćwiczenie:-).
public abstract class Gra
{
    private int liczbaGraczy;
    protected abstract void InicjalizujGrę();
    protected abstract void ZróbRuch(int gracz);
    protected abstract bool KoniecGry();
    protected abstract void WyświetlZwycięzcę();
    /// 
    /// Metoda szablonu
    /// 
    public void ZagrajRaz(int liczbaGraczy)
    {
        this.liczbaGraczy = liczbaGraczy;
        InicjalizujGrę();
        int j = 0;
        while (!KoniecGry())
        {
            ZróbRuch(j);
            j = (j + 1) % liczbaGraczy;
        }
        WyświetlZwycięzcę();
    }
}
public class Monopoly : Gra
{
    #region Gra
    protected override void InicjalizujGrę()
    {
        throw new Exception("The method or operation is not implemented.");
    }
    protected override void ZróbRuch(int gracz)
    {
        throw new Exception("The method or operation is not implemented.");
    }
    protected override bool KoniecGry()
    {
        throw new Exception("The method or operation is not implemented.");
    }
    protected override void WyświetlZwycięzcę()
    {
        throw new Exception("The method or operation is not implemented.");
    }
    #endregion
}
public class Szachy : Gra
{
    #region Gra
    protected override void InicjalizujGrę()
    {
        throw new Exception("The method or operation is not implemented.");
    }
    protected override void ZróbRuch(int gracz)
    {
        throw new Exception("The method or operation is not implemented.");
    }
    protected override bool KoniecGry()
    {
        throw new Exception("The method or operation is not implemented.");
    }
    protected override void WyświetlZwycięzcę()
    {
        throw new Exception("The method or operation is not implemented.");
    }
    #endregion
}
Na koniec diagram klas :

1)
2)


Drugi obraz dosyć dobrze odzwierciedla, to co się działo powyżej.

Dekorator (obiektowy)

Dekorator (Decorator)


Umożliwia dodanie nowej funkcjonalności dynamicznie podczas działania programu.

Wzorzec dekoratora polega na opakowaniu oryginalnej klasy w nową klasę "dekorującą". Zwykle przekazuje się oryginalny obiekt jako parametr konstruktora dekoratora, metody dekoratora wywołują metody oryginalnego obiektu i dodatkowo implementują nową funkcję.

Dekoratory są alternatywą dla dziedziczenia. Dziedziczenie rozszerza zachowanie klasy w trakcie kompilacji, w przeciwieństwie do dekoratorów, które rozszerzają klasy w czasie działania programu.

Jest to dosyć jasny wzorzec wiec szybko przejde do przykładu.

1. Na początek tworzymy interfejs Okno.
interface Okno {
    public void rysuj(); // rysuje Okno na ekranie
    public String pobierzOpis(); // zwraca opis Okna
}
2. Implementacja zwykłego okna bez pasków przewijania.
class ZwykłeOkno implements Okno {
    public void rysuj() {
        // rysuj okno
    }
 
    public String pobierzOpis() {
        return "zwykłe okno";
    }
}
3. Abstrakcyjna klasa dekorator - implementuje interfejs Okno. Klasa OknoDekorator przechowuje w sobie jako pole Okno. To właśnie okno będzie dekorowane, dlatego więc do konstruktora klasy przekazane zostanie okno, które będzie mialo zostać udekorowane. Jest to klasa abstrakcyjna wiec nie jest wymagane, aby implementowac wszystkie metody z interfejsu Okno.
abstract class OknoDekorator implements Okno {
    protected Okno dekorowaneOkno; // dekorowane Okno
 
    public OknoDekorator(Okno dekorowaneOkno) {
        this.dekorowaneOkno = dekorowaneOkno;
    }
}
4. Czas na pierwszy dekorator dodający pionowe paski przewijania. Każdy dekorator na swój sposób implementuje/dekoruje metode rysuj(). W klasie PionowePrzewijanieDekorator w metodzie rysuj() wywolujemy tez metode rysuj() na rzecz obiektu dekorowaneOkno.
class PionowePrzewijanieDekorator extends OknoDekorator {
    public PionowePrzewijanieDekorator (Okno dekorowaneOkno) {
        super(dekorowaneOkno);
    }
 
    public void rysuj() {
        rysujPionowyPasekPrzewijania();
        dekorowaneOkno.rysuj();
    }
 
    private void rysujPionowyPasekPrzewijania() {
        // rysuj pionowy pasek przewijania
    }
 
    public String pobierzOpis() {
        return dekorowaneOkno.pobierzOpis() + ", z pionowym paskiem przewijania";
    }
}
5. Drugi inny dekorator dodający poziome paski przewijania.
class PoziomePrzewijanieDekorator extends OknoDekorator {
    public PoziomePrzewijanieDekorator (Okno dekorowaneOkno) {
        super(dekorowaneOkno);
    }
 
    public void rysuj() {
        rysujPoziomyPasekPrzewijania();
        dekorowaneOkno.rysuj();
    }
 
    private void rysujPoziomyPasekPrzewijania() {
        // rysuj poziomy pasek przewijania
    }
 
    public String pobierzOpis() {
        return dekorowaneOkno.pobierzOpis() + ", z poziomym paskiem przewijania";
    }
}
6. Na sam koniec program testowy, który tworzy obiekt klasy Okno, dekoruje go poziomymi i pionowymi paskami przewijania i wypisuje jego opis. Prosze zwrócic uwage na sposób w jaki tworzymy dekorowane okno. Najpierw wywolujemy new dla pierwszego dekoratora, nastepnie do konstruktora tego dekoratora przekazujemy new drugiego dekoratora. Wszystko sie zgadza oba dekoratory przyjmuja jako argument do konstruktora obiekt klasy Okno oraz oba dekoratory dziedzicza po klasie OknoDekorator, a ta z koleji implementuje interfejs Okno. Na samo koniec inicjalizacji dekorowanego okna do konstruktora drugiego dekoratora przekazujemy zwykłeOkno. W następnej linijce pobieramy opis. Można dzięki takiemu zabiegowi łatwo sprawdzić czy wszystko się zgadza.
public class DekorowaneOknoTest {
    public static void main(String[] args) {
        // utwórz dekorowane Okno z poziomymi i pionowymi paskami przewijania
        Okno dekorowaneOkno = new PoziomePrzewijanieDekorator(
                new PionowePrzewijanieDekorator(new ZwykłeOkno()));
 
        // wypisz opis Okna
        System.out.println(dekorowaneOkno.pobierzOpis());
    }
}
Dekorator jest jednym z protszych wzorców. Myśle, że powyższy przykład dość dobrze odzwierciedlił zasade działania tego wzroca. Na koniec tradycyjnie diagram klas :


Metoda wytwórcza (klasowy)

Metoda wytwórcza (Factory method)

Umożliwia klasom przekazanie tworzenia egzemplarzy podklasom. Jak sama nazwa wskazuje wzorzec ten sluży do tworzenia obiektów. Jest podobny do wzorca budowniczy oraz fabryki abstrakcyjnej. Poniżej opisze różnice co wyróżnia metode wytwórczą od powyższych wzorców. Zanim jednak to zrobie wydaje mi się, że najłatwiej będzie zacząć od przykładu.
Podobnie jak dla budowniczego przykład będzie związany z pizza.


1. Na początek tworzymy klase abstrakcyjną Pizza wraz z metoda getPrice() zracającą cenę danej pizzy. Narazie jednak nie znamy ceny więc metoda pozostaje abstrakcyjna.
abstract class Pizza {
    public abstract double getPrice();
}
2. Konkretny rodzaj pizzy ze zdefiniowaną ceną.
class HamAndMushroomPizza extends Pizza {
    private double price = 8.5;
 
    public double getPrice() {
        return price;
    }
}
3. Analogicznie jak powyżej inny rodzaj pizzy z inna ceną.
class DeluxePizza extends Pizza {
    private double price = 10.5;
 
    public double getPrice() {
        return price;
    }
}
4. Nie wymaga komentarza.
class HawaiianPizza extends Pizza {
    private double price = 11.5;
 
    public double getPrice() {
        return price;
    }
}
5. Teraz natomiast tworzymy Fabryke pizzy, w której umieszczony został typ enum z konkretnymi rodzajami pizzy, takimi jak zdefiniowaliśmy powyżej, inaczej nie miałoby to sensu. W klasie tej jak widać poniżej mamy metode createPizza(). Jest to metoda statyczna, która jako argument przyjmuje konkretny rodzaj pizzy. Prosze zwrócić uwagę, iż rodzaj ten jest enumem zdefiniowanym właśnie w tej klasie. Na podstawie typu jaki metoda otrzymała zwraca konkretny rodzaj pizzy. Jeżeli argument będzie niepoprawny rzuci wyjątkiem. Można to zmienić. Każdy może na własny sposób zaimplementować taki przypadek zdarzenia:-). Ta klasa to "engine" całego wzorca. Tutaj widać różnice jaką wyróżnia się ten wzorzec.
class PizzaFactory {
    public enum PizzaType {
        HamMushroom,
        Deluxe,
        Hawaiian
    }

    public static Pizza createPizza(PizzaType pizzaType) {
        switch (pizzaType) {
            case HamMushroom:
                return new HamAndMushroomPizza();
            case Deluxe:
                return new DeluxePizza();
            case Hawaiian:
                return new HawaiianPizza();
        }
        throw new IllegalArgumentException("The pizza type " + pizzaType + " is not recognized.");
    }
}
6. Ostatni fragment kodu to testowanie/main programu. Tutaj można sprawdzić czy wszystko działa według naszych oczekiwań. Jest to jakaś możliwość przetestowania programu. Każdy może zrobić to na własny sposób. Nasz for, który jest de-facto foreach-em jako typ iterowany wybiera enuma z klasy PizzaFactory. Możemy o to poprosić, ponieważ jest to enum. Będziemy iterowali po całym zbiorze enuma, czyli stworzymy każdy rodzaj pizzy i podamy jego cene. Następnie korzystamy z metody statycznej z klasy PizzaFactory przekazując jej typ pizzy jaki chcemy stworzyć i od razu w jednej linijce prosimy o podanie ceny za dany rodzaj pizzy.
class PizzaLover {
    /**
     * Create all available pizzas and print their prices
     */
    public static void main (String args[]) {
        for (PizzaFactory.PizzaType pizzaType : PizzaFactory.PizzaType.values()) {
            System.out.println("Price of " + pizzaType + " is " + PizzaFactory.createPizza(pizzaType).getPrice());
        }
    }
}
Jak już wcześniej wspominałem wzorzec metoda wytwórcza jest podobny do przedwszystkim wzorca fabryka abstrakcyjna oraz budowniczego. Różnice, co do fabryki abstrakcyjnej są przedstawione powyżej w punkcie 5. W metodzie wytwórczej metoda createPizza jest metoda statyczna. Podobnie jak metoda GUIFactory z rozdziału o fabryce abstrakcyjnej. Jednak różnica polega na tym, iż teraz metoda createPizza() przyjmuje rodzaj konkretnej pizzy. Meoda GUIFactory sama na podstawie tego, co ustaliła wewnatrz samej siebie określała jakiego rodzaju przycisk ma stworzyć. Tak więc gdyby przełożyć przykład z fabryki abstrakcyjnej do metody wytwórczej musielibyśmy najpierw stworzyć odpowiedniego enuma z każdym rodzajem przycisku. Następnie przerobić metodę, tak by przyjmowała jako argument naszego enuma i na podstawie tego enuma zwracała odpowiedni typ przycisku. Podsumowywując zachęcam do przeczytania rodziału o fabryce abstrakcyjnej oraz budowniczym dla lepszego zrozumienia powyższych jak że podobnych do siebie wzorców. Różnice pomiędzy fabryką, a budowniczym opisałem w rozdziale o fabryce, dlatego warto choć raz przeczytać.

Na zakończenie rozważań o metodzie fabrykującej przedstawiam diagram klas:


Fabryka abstrakcyjna (obiektowy)

Fabryka Abstrakcyjna (Abstract Factory)


Skupia się na tworzeniu obiektów jednego typu (tej samej rodziny) bez specyfikowania ich konkretnych klas. Umożliwia jednemu obiektowi tworzenie różnych, powiązanych ze sobą podobiektów.

Czas na przykład, który mam nadzieje wyjaśni zasadę działania tego wzorca. Będzie to przykład ilustrujący fabrykę tworzenia przycisków pod różne systemy operacyjne.

1. Na początku tworzymy fabrykę, która posiada metodę statyczną getFactory() i zwraca odpowiedni typ już konkretnej fabryki na podstawie systemu, który zczytuje z pliku. Można implementować poniższy fragment na wiele różnych sposobów. Nie jest powiedziane, że za każdym razem budowa metody ma tak samo wyglądać. Chodzi mi tutaj np. o metodę readFromConfigFile(). W naszym przykładzie informacje o systemie na jakim pracujemy możemy równie dobrze zczytywać w jakiś inny sposób, albo mieć to w jakiś sposób zahardcodowane. Oprócz metody getFactory() mamy również metodę createButton(), którą każda już konkretna fabryka będzie implementowała na swój sposób.
/* Przykład GUIFactory */
abstract class GUIFactory {
    public static GUIFactory getFactory() {
        int sys = readFromConfigFile("OS_TYPE");
        if (sys == 0) {
            return new WinFactory();
        } else {
            return new OSXFactory();
        }
    }
    public abstract Button createButton();
}
2. Teraz czas na konkretna fabrykę - WinFactory, która rozszerza klasę abstrakcyjną GUIFactory i zarazem musi oraz implementuje metode createButton() zwracając tym samym już konkretny typ przycisku, a dokładniej pod system Windows.
class WinFactory extends GUIFactory {
    public Button createButton() {
        return new WinButton();
    }
}
3. Następnie analogicznie jak powyżej klasa - OSXFactory, która zwraca przycisk pod system OS.
class OSXFactory extends GUIFactory {
    public Button createButton() {
        return new OSXButton();
    }
}
4. Tworzymy klase abstrakcyjną Button. Jedyne, co w niej będziemy przechowywać to metoda abstrakcyjną paint(), która będzie implementowana już przez konkretne klasy przycisków pod Windowsa oraz OS-a.
abstract class Button {
    public abstract void paint();
}
5. Klasa WinButton rozszerza klasę Button i implemetuje metode paint(). Na swój sposób klasa ta będzie rysowała przycisk pod system Windows.
class WinButton extends Button {
    public void paint() {
        System.out.println("Przycisk WinButton");
    }
}
6. Analogicznie jak powyżej klasa OSXButton będzie rysowała przycisk w jakiś inny sposób. Oczywiście metody paint() w obu powyższych klasach nic nie robią oprócz wyświetlania jakiegoś komunikatu w konsoli, ale chodzi oczywiście tylko o zademonstrowanie mechanizmu działania wzorca:-).
class OSXButton extends Button {
    public void paint() {
        System.out.println("Przycisk OSXButton");
    }
}
7. Czas na ostatni fragment kodu - przetestowanie naszych klas. Najpierw tworzymy obiekt klasy GUIFactory. Metoda getFactory() była metodą statyczną więc bez problemów możemy ją wykonać nie mając obiektu klasy GUIFactory. Jak wcześniej napisałem metoda ta zwraca konkretną fabrykę, na podstawie rodzaju systemu jaki przeczyta z pliku konfiguracyjnego. W kolejnej linijce działamy wykorzystując polimorfizm, poniewaz nasz obiekt factory jest już konkretną implementacją i dlatego możemy na rzecz tego obiektu wywołać metodę createButton(), która zwróci nam konkretny typ przycisku. W następnej linijce możemy wyświetlić nasz przycisk i sprawdzić czy działa nasz program poprawnie. I tak jak jest napisane w komentarzu zostanie wyświetlony odpowiedni tekst "Przycisk WinButton" lub "Przycisk OSXButton". W zależności od tego jaki typ systemu będzie zdefiniowany w pliku konfiguracyjnym.
public class DekorowaneOknoTest {
    public static void main(String[] args) {
        // utwórz dekorowane Okno z poziomymi i pionowymi paskami przewijania
        Okno dekorowaneOkno = new PoziomePrzewijanieDekorator(
        new PionowePrzewijanieDekorator(new ZwykłeOkno()));
        // wypisz opis Okna
        System.out.println(dekorowaneOkno.pobierzOpis());
    }
}
Wzorzec Fabryka Abstrakcyjna jest podobnym wzorcem do wzorca Budowniczy oraz Metoda Wytwórcza i może się z tymi wzorcami mylić. Jaka jest więc różnica? Zachęcam do przeczytania postu o budowniczym jeżeli chce się przestudiować dokładne działanie tego wzorca. Jednak główna różnica polega na tym, iż w budowniczym mieliśmy w samej klasie PizzaBuilder (klasie abstrakcyjnej budowniczego) obiekt klasy Pizza (abstrakcyjnej klasy, której obiekty budowaliśmy). W fabryce nie mamy obiektu klasy, której produkujemy (naszego przycisku), a jedynie metode getFactory(), która zwraca konkretna fabrykę, a ta natomiast tworzy dynamicznie obiekt przyciku. Jakby to przełożyć na budowniczego to w budowniczym mielibyśmy obiekt abstrakcyjny przycisku i nastepnie byśmy go budowali, tzn. dodawali jakieś właściwości pod konkretny system. Tak jak było z pizza. Mieliśmy obiekt pizzy w abstrakcyjnej klasie budowniczego, a w konkretnych tylko budowalismy daną pizze. Budowaliśmy ciasto, sos, dodatki dla konkretnego rodzaju pizzy. W przypadku przyciku moglibyśmy "budować" np. tło porzycisku. W fabryce tego "przycisku" nie mamy, tej "pizzy" nie mamy, jako obiektu jakiejś klasy abstrakcyjnej tylko dopiero w momencie, gdy wiemy już jaki przycisk konkretnie stworzyć piszemy odpowiednio :
return new WinButton()
lub
return new OSXButton()
W przypadku "pizzy" byłoby np.
return new HawaiiPizza().
Mam nadzieje, że pomimo tego, że troche się rozpisałem każdy zrozumie jaka jest różnica pomiędzy tymi dwoma wzorcami. Zachęcam do przeczytania oddzielnych wpisów na temat budowniczego oraz metody fabrykującej.

Teraz czas na diagram klas tego wzorca.