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.