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

Brak komentarzy:

Prześlij komentarz