Visitor pattern is a behavioral pattern that allows addition of new features to a class without actually modifying the class. Visitor pattern is one way of implementing the Open/ Close principle (which is one of the SOLID principles).
Lets learn more about this pattern using an example. Say we are a software company and one of our clients wants us to develop a simple tool that allows the end-user to draw geometric shapes and save them to a file. At the time of save, the user should have the option to specify the file format. The tool should support the shapes – Circle, Square and the formats – png, jpeg. Lets see how we can go about developing the tool.
We can create an abstract class called Shape. We will need separate methods for saving files in different formats for each shape. This is so because each format will require its own set of variables and logic to write to a file which can vary depending on the shape.
class Shape{ protected: string name; virtual void draw()=0;//draws the shape on the canvas virtual saveAsPng()=0;//saves the file with .png extension virtual saveAsJpeg()=0;//saves the file with .jpeg extension };
Circle and Square will need to extend the Shape class and implement all of its methods.
class Circle: public Shape{ public: Circle(){name = "Circle";} draw(); saveAsPng(); saveAsJpeg(); }; class Square : public Shape{ public: Square(){name = "Square";} draw(); saveAsPng(); saveAsJpeg(); };
Everything is perfect. We designed and developed the tool, tested the features and gave our tool to the client and they were happy, end of story!.
Not really! After a few months, our client wants us to add more file formats to the tool. Now, we need to modify all of our classes – Shape, Circle and Square. We need to add a new method for the new file format in all of these classes. Imagine the situation if we had several shapes instead of the two. Anyway, we go through the pain, modify the classes and deliver the tool to the client.
Again after some months, they want the tool to support additional file formats. It is high time we realized that the client requirements are perennial. It is not practical for us to modify all of the classes each time our client approaches us with a new requirement. So the best option would be to change the design of the tool so that new changes can be incorporated with little effort. This is where Visitor Pattern can help us.
After we re-design our tool as per the visitor pattern, the classes would look as shown below.
class Shape{ protected: string name; virtual void draw()=0; // draws the shape on the canvas virtual accept(SaveTypeVisitor visitor) = 0; }; class Circle: public Shape{ public: Circle(){name = "Circle";} draw(); accept(SaveTypeVisitor visitor){ visitor.visit(this); } }; class Square : public Shape{ public: Square(){name = "Square";} draw(); accept(SaveTypeVisitor visitor){ visitor.visit(this); } }; class saveTypeVisitor{ protected: virtual visit(Shape shape)=0; }; class pngTypeVisitor:public saveTypeVisitor{ public: visit(Shape shape){ if(shape.name == "Circle"){ //do Something } else if (shape.name=="Square"){ //do Something } } }; class pngTypeVisitor:public saveTypeVisitor{ public: visit(Shape shape){ if(shape.name == "Circle"){ //do Something } else if (shape.name=="Square"){ //Do Something } } };
As you can see, there are a few new classes in our revamped design. If you observe closely, any new file format can now be easily implemented in the shape classes without even touching them. We will only need to create a single new class by extending the saveTypeVisitor.
If the end-user wants to save Circle in a .png format, we can call the accept() method of the Circle class with pngTypeVisitor
as its argument.
Therefore , visitor pattern consists of two types of classes: visitor class and element class. In our example, saveTypeVisitor
is the abstract visitor class and pngTypeVisitor
and jpegTypeVisitor
are its extensions whereas Shape
is the abstract element class and Circle
and Sqaure
are its extensions. Visitor pattern lets us define new features in element classes with the help of visitor classes without making any changes to the element classes.
Leave a Reply