I keep seeing references to the visitor pattern in blogs but I've got to admit, I just don't get it. I read the wikipedia article for the pattern and I understand its mechanics but I'm still confused as to when I'd use it.
As someone who just recently really got the decorator pattern and is now seeing uses for it absolutely everywhere I'd like to be able to really understand intuitively this seemingly handy pattern as well.
Visitor design pattern is one of the behavioral design patterns. It is used when we have to perform an operation on a group of similar kind of Objects. With the help of visitor pattern, we can move the operational logic from the objects to another class.
The Visitor design pattern is one of the twenty-three well-known Gang of Four design patterns that describe how to solve recurring design problems to design flexible and reusable object-oriented software, that is, objects that are easier to implement, change, test, and reuse.
If a problem has two solutions, one that fits in ten lines of code, and another one with hundreds of lines of code along with a pattern, please consider not using the pattern. Their presence isn't a quality measurement.
The Visitor pattern represents an operation to be performed on the elements of an object structure without changing the classes on which it operates.
I'm not very familiar with the Visitor pattern. Let's see if I got it right. Suppose you have a hierarchy of animals
class Animal { }; class Dog: public Animal { }; class Cat: public Animal { };
(Suppose it is a complex hierarchy with a well-established interface.)
Now we want to add a new operation to the hierarchy, namely we want each animal to make its sound. As far as the hierarchy is this simple, you can do it with straight polymorphism:
class Animal { public: virtual void makeSound() = 0; }; class Dog : public Animal { public: void makeSound(); }; void Dog::makeSound() { std::cout << "woof!\n"; } class Cat : public Animal { public: void makeSound(); }; void Cat::makeSound() { std::cout << "meow!\n"; }
But proceeding in this way, each time you want to add an operation you must modify the interface to every single class of the hierarchy. Now, suppose instead that you are satisfied with the original interface, and that you want to make the fewest possible modifications to it.
The Visitor pattern allows you to move each new operation in a suitable class, and you need to extend the hierarchy's interface only once. Let's do it. First, we define an abstract operation (the "Visitor" class in GoF) which has a method for every class in the hierarchy:
class Operation { public: virtual void hereIsADog(Dog *d) = 0; virtual void hereIsACat(Cat *c) = 0; };
Then, we modify the hierarchy in order to accept new operations:
class Animal { public: virtual void letsDo(Operation *v) = 0; }; class Dog : public Animal { public: void letsDo(Operation *v); }; void Dog::letsDo(Operation *v) { v->hereIsADog(this); } class Cat : public Animal { public: void letsDo(Operation *v); }; void Cat::letsDo(Operation *v) { v->hereIsACat(this); }
Finally, we implement the actual operation, without modifying neither Cat nor Dog:
class Sound : public Operation { public: void hereIsADog(Dog *d); void hereIsACat(Cat *c); }; void Sound::hereIsADog(Dog *d) { std::cout << "woof!\n"; } void Sound::hereIsACat(Cat *c) { std::cout << "meow!\n"; }
Now you have a way to add operations without modifying the hierarchy anymore. Here is how it works:
int main() { Cat c; Sound theSound; c.letsDo(&theSound); }
The reason for your confusion is probably that the Visitor is a fatal misnomer. Many (prominent1!) programmers have stumbled over this problem. What it actually does is implement double dispatching in languages that don't support it natively (most of them don't).
1) My favourite example is Scott Meyers, acclaimed author of “Effective C++”, who called this one of his most important C++ aha! moments ever.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With