在你告诉我已经存在类似的问题之前,是的,我知道,我已经阅读了it。 但那里的问题集中在何时,我对为什么感兴趣。
我知道事情是如何运作的。经典的动物,狗,猫示例总是像魅力一样。
事情就是这段代码
int main()
{
Cat c;
Sound theSound;
c.letsDo(&theSound);
}
对我来说似乎很不自然。为什么呢?
我的意思是,是的,这样我的狗和猫模型无差别(我第一次使用英语btw中的那个词)因为真正的实现隐藏在Sound类之下但是不是'这只是一种权衡代码的方法吗?多态性不足以做这样的事吗?
对我而言,不同之处在于,使用多态性,您必须编辑每个类(但模型保持不变,对吧?)而您只需使用访问者设计模式编辑一个类。
答案 0 :(得分:8)
访问者模式允许您执行某些操作,而这些操作依赖于多态性不会:使用意外的用例。如果您正在编写库,这是一个重点。让我详细说明一下:
考虑使用访问者模式的经典示例,即对某些abstract syntax树的节点上的操作。例如,要添加一些细节,您刚刚为SQL编写了一个解析器库,它接受字符串,解析它们,并返回它在输入中找到的东西的AST。除非您能够预测客户端代码可能具有此类AST的所有潜在用例,否则您必须提供一种“通用”方式来处理AST。提供类似DOM的访问器功能(getNodeType
,getParentNode
,getPreviousNode
)是一种方法。这里的问题是,这会给您图书馆的客户带来沉重的负担,因为他们需要自己进行调度。更重要的是,他们需要非常详细地了解每个可能的节点类型要遵循的指针:
void
walk_tree(AstNode* node)
{
switch( node->getNodeType() ) {
case SELECT_NODE:
for( AstNode* child = node->getFirstChild(); child; child = child->getNextNode() ) {
walk_tree(child);
}
break;
...
}
}
访问者模式将这种负担从客户端转移到库中。
答案 1 :(得分:3)
假设你在一个你不拥有的库中定义了一些基本的东西,你需要扩展它。像:
// In base lib:
interface ISomething {
void DoSomething();
}
class Something1 : ISomething {
// ...
}
class Something2 : ISomething {
// ...
}
Polymorphism允许您定义可以执行操作的新事物:
// In your lib:
class MySomething : ISomething {
}
现在基础库可以与您的MySomething
一起使用,就像它已定义它一样。 不让你做的是添加新的操作。 DoSomething
是ISomething
我们唯一能做的事情。访客模式解决了这个问题。
缺点是使用访问者模式花费你能够定义像我们刚刚展示的新类型。大多数语言允许您轻松添加操作或类型,而不是两者都称为expression problem。
访问者模式很酷,但我实际上从未在实现编译器之外找到它。
答案 2 :(得分:2)
访客模式非常有用。
使用它至少有三个重要原因:
减少代码的扩散,这在数据结构发生变化时略有不同。
将相同的计算应用于多个数据结构,而不更改实现计算的代码。
在不更改旧代码的情况下向旧版库添加信息。
请查看an article I've written about this。
干杯
答案 3 :(得分:1)
当我有一个对象树时,我使用了访问者模式,需要以多种方式打印内容。逗号sep,XML,等等。我使用了访问者模式并创建了CommaSepVisitor,XMLVisitor和HTMLVisitor类,而不是为每个输出格式添加一个新的打印方法。树代码从未改变,因为我添加了更多访问者类型,所以我从未介绍过bug。访客本身很容易写。