使用访客设计模式的一个很好的理由?

时间:2010-09-10 14:51:05

标签: design-patterns visitor-pattern

在你告诉我已经存在类似的问题之前,是的,我知道,我已经阅读了it。 但那里的问题集中在何时,我对为什么感兴趣。

我知道事情是如何运作的。经典的动物,狗,猫示例总是像魅力一样。

事情就是这段代码

int main()
{
    Cat c;
    Sound theSound;
    c.letsDo(&theSound);
}

对我来说似乎很不自然。为什么呢?

我的意思是,是的,这样我的狗和猫模型无差别(我第一次使用英语btw中的那个词)因为真正的实现隐藏在Sound类之下但是不是'这只是一种权衡代码的方法吗?多态性不足以做这样的事吗?

对我而言,不同之处在于,使用多态性,您必须编辑每个类(但模型保持不变,对吧?)而您只需使用访问者设计模式编辑一个类。

4 个答案:

答案 0 :(得分:8)

访问者模式允许您执行某些操作,而这些操作依赖于多态性不会:使用意外的用例。如果您正在编写库,这是一个重点。让我详细说明一下:

考虑使用访问者模式的经典示例,即对某些abstract syntax树的节点上的操作。例如,要添加一些细节,您刚刚为SQL编写了一个解析器库,它接受字符串,解析它们,并返回它在输入中找到的东西的AST。除非您能够预测客户端代码可能具有此类AST的所有潜在用例,否则您必须提供一种“通用”方式来处理AST。提供类似DOM的访问器功能(getNodeTypegetParentNodegetPreviousNode)是一种方法。这里的问题是,这会给您图书馆的客户带来沉重的负担,因为他们需要自己进行调度。更重要的是,他们需要非常详细地了解每个可能的节点类型要遵循的指针:

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一起使用,就像它已定义它一样。 让你做的是添加新的操作DoSomethingISomething我们唯一能做的事情。访客模式解决了这个问题。

缺点是使用访问者模式花费你能够定义像我们刚刚展示的新类型。大多数语言允许您轻松添加操作或类型,而不是两者都称为expression problem

访问者模式很酷,但我实际上从未在实现编译器之外找到它。

答案 2 :(得分:2)

访客模式非常有用。

使用它至少有三个重要原因:

  1. 减少代码的扩散,这在数据结构发生变化时略有不同。

  2. 将相同的计算应用于多个数据结构,而不更改实现计算的代码。

  3. 在不更改旧代码的情况下向旧版库添加信息。

  4. 请查看an article I've written about this

    干杯

答案 3 :(得分:1)

当我有一个对象树时,我使用了访问者模式,需要以多种方式打印内容。逗号sep,XML,等等。我使用了访问者模式并创建了CommaSepVisitor,XMLVisitor和HTMLVisitor类,而不是为每个输出格式添加一个新的打印方法。树代码从未改变,因为我添加了更多访问者类型,所以我从未介绍过bug。访客本身很容易写。