Visitor Pattern
Introduction:
Visitor is a creational design pattern used to decouple the logic or algorithm from the object on which it operates. The logic is moved to separate class called visitor. Each visitor is responsible for specific operation.
When to use,
"When we to execute some algorithm on the object without creating the tight coupling between the object and algorithm(or without modifying the object)"
We have a list of shapes , and we want to export X,Y coordinates of the shapes into xml format. The direct approach is to add export function in the object. But it creates tight coupling between the object and algorithm on which it operates.
With visitor pattern, we can remove the tight coupling by separating the algorithm from the object on which it operates. If any new algorithm need to add in the future, it will be added separately without changing the underlying code. This leverages the Open/Closed principle (i.e.) classes should be open for extension but closed for modification.
How to use,
- Declare "Visitor" abstract class (or) interface with require "visit" method to element derived class.
- Create concrete visitor class ,and provide the implementation for all the visit method for all the elements.
- Add "accept" method in element interface class.
- Provide implementation of "accept" method in all the concrete element class.
- Client code creates the object of concrete visitor class , and call the accept method.
UML diagram:
Implementation:
Sourcecode: https://github.com/krishnaKSA/design_patterns_cplusplus/blob/main/VisitorPattern/VisitorPattern.hpp
Class Diagram:
Visitor Interface class:
//interface class
class visitor
{
public:
virtual void visitCircle(Circle* c) = 0;
virtual void visitDot(Dot* d) = 0;
virtual void visitSquare(Square* s) = 0;
virtual void visitRectangle(Rectangle* r) = 0;
};
Concrete Visitor class:
class visitorShape : public visitor
{
public:
void visitCircle(Circle* c) override
{
cout<<"Dumped Circle details "<<endl;
}
void visitDot(Dot* d) override
{
cout<<"Dumped Dot details "<<endl;
}
void visitSquare(Square* s) override
{
cout<<"Dumped Square details "<<endl;
}
void visitRectangle(Rectangle* r) override
{
cout<<"Dumped Rectangle details "<<endl;
}
};
Element Abstract Class:
//Element abstract class
class Shape
{
protected:
int xcord;
int ycord;
public:
virtual void draw() = 0;
virtual void accept(visitor* v) = 0;
};
Element concrete classes:
//element concrete class
class Circle : public Shape
{
public:
void draw()
{
cout<<"circle placed in xcord "<<xcord<<" ycord "<<ycord<<endl;
}
void accept(visitor* v)
{
v->visitCircle(this);
}
};
class Dot : public Shape
{
public:
void draw()
{
cout<<"Dot placed in xcord "<<xcord<<" ycord "<<ycord<<endl;
}
void accept(visitor* v)
{
v->visitDot(this);
}
};
class Square : public Shape
{
public:
void draw()
{
cout<<"Square placed in xcord "<<xcord<<" ycord "<<ycord<<endl;
}
void accept(visitor* v)
{
v->visitSquare(this);
}
};
class Rectangle : public Shape
{
public:
void draw()
{
cout<<"Rectangle placed in xcord "<<xcord<<" ycord "<<ycord<<endl;
}
void accept(visitor* v)
{
v->visitRectangle(this);
}
};
Application:
class ApplicationExport
{
private:
vector<Shape*> shapeList;
public:
ApplicationExport()
{
shapeList = {new Circle, new Dot, new Rectangle, new Square};
}
void exportInXMLFormat(visitor* v)
{
for(Shape* s:shapeList)
{
s->accept(v);
}
}
};
Client code:
void testClientCode()
{
ApplicationExport* appl = new ApplicationExport();
visitor* visit = new visitorShape;
appl->exportInXMLFormat(visit);
}