问题描述
我一直在博客中看到对访客模式的参考,但我必须承认,我只是不明白.我读了 wikipedia文章的图案,我仍然理解其机制,但我仍然感到困惑.当我使用它时.
作为最近真正真正获得装饰的人的人,现在绝对可以看到它的用途,我希望能够真正地理解这种看似方便的图案.
推荐答案
我对访客模式不太熟悉.让我们看看我是否正确.假设您有动物的层次结构
class Animal { }; class Dog: public Animal { }; class Cat: public Animal { };
(假设它是具有完善接口的复杂层次结构.)
现在,我们想为层次结构添加一个新操作,即我们希望每只动物发出声音.至于层次结构很简单,您可以使用直型多态性来完成:
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"; }
但是以这种方式进行操作,每次您要添加一个操作时,都必须将接口修改为层次结构的每个类.现在,假设您对原始接口感到满意,并且想对其进行最少的修改.
访问者模式允许您在合适的类中移动每个新操作,并且您需要仅扩展层次结构的界面一次.我们开始做吧.首先,我们定义了一个抽象操作( gof )中有一种方法层次结构中的每个类:
class Operation { public: virtual void hereIsADog(Dog *d) = 0; virtual void hereIsACat(Cat *c) = 0; };
然后,我们修改层次结构以接受新操作:
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); }
最后,我们实施了实际操作, 否未修改CAT和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"; }
现在,您可以在不再修改层次结构的情况下添加操作. 这是它的工作方式:
int main() { Cat c; Sound theSound; c.letsDo(&theSound); }
其他推荐答案
您混淆的原因可能是访客是致命的错误称呼.许多(突出的 1 !)程序员偶然发现了这个问题.它实际上要做的是实现 double Dispatching 不支持它的语言(大多数人)不要).
1)我最喜欢的例子是斯科特·迈耶斯(Scott Meyers),他是"有效C ++"的作者,他称此为他的最重要的C ++ aha!矩 ever .
其他推荐答案
这里的每个人都是正确的,但是我认为它无法解决"何时".首先,来自设计模式:
访客让您定义一个新的 不更改课程的操作 其操作的元素.
现在,让我们考虑一个简单的类层次结构.我有第1、2、3和4类,以及方法A,B,C和D.将它们像电子表格一样放置:类是行,方法是列.
.现在,面向对象的设计假定您比新方法更有可能成长新的类,因此可以说,添加更多的行更容易.您只需添加一个新类,指定该类的不同之处,然后继承其余的.
有时,这些类是相对静态的,但是您需要经常添加更多方法 - 添加列. OO设计中的标准方式是在所有类中添加此类方法,这可能是昂贵的.访问者的模式使这简单.
顺便说一句,这是Scala模式与打算求解的问题.
问题描述
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.
推荐答案
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.
其他推荐答案
Everyone here is correct, but I think it fails to address the "when". First, from Design Patterns:
Visitor lets you define a new operation without changing the classes of the elements on which it operates.
Now, let's think of a simple class hierarchy. I have classes 1, 2, 3 and 4 and methods A, B, C and D. Lay them out like in a spreadsheet: the classes are lines and the methods are columns.
Now, Object Oriented design presumes you are more likely to grow new classes than new methods, so adding more lines, so to speak, is easier. You just add a new class, specify what's different in that class, and inherits the rest.
Sometimes, though, the classes are relatively static, but you need to add more methods frequently -- adding columns. The standard way in an OO design would be to add such methods to all classes, which can be costly. The Visitor pattern makes this easy.
By the way, this is the problem that Scala's pattern matches intends to solve.