Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Design patterns 使用访问者设计模式的一个很好的理由?_Design Patterns_Visitor Pattern - Fatal编程技术网

Design patterns 使用访问者设计模式的一个很好的理由?

Design patterns 使用访问者设计模式的一个很好的理由?,design-patterns,visitor-pattern,Design Patterns,Visitor Pattern,在你告诉我已经有一个类似的问题之前,是的,我知道,我已经读过了。 但问题集中在何时,我感兴趣的是为什么 我知道事情是如何运作的。经典的动物、狗、猫的例子总是很有魅力 问题是这个代码 int main() { Cat c; Sound theSound; c.letsDo(&theSound); } 我觉得很不自然。为什么? 我的意思是,是的,这样我就没有区分我的狗和猫模型(我第一次在英语中使用这个词,顺便说一句),因为真正的实现隐藏在Sound类下,但这不只是一

在你告诉我已经有一个类似的问题之前,是的,我知道,我已经读过了。 但问题集中在何时,我感兴趣的是为什么

我知道事情是如何运作的。经典的动物、狗、猫的例子总是很有魅力

问题是这个代码

int main()
{
    Cat c;
    Sound theSound;
    c.letsDo(&theSound);
}
我觉得很不自然。为什么?

我的意思是,是的,这样我就没有区分我的狗和猫模型(我第一次在英语中使用这个词,顺便说一句),因为真正的实现隐藏在Sound类下,但这不只是一种减轻代码负担的方法吗?多态性还不够做这样的事情吗


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

访问者模式允许您做一些事情,而仅仅依赖多态性是无法做到的:处理意外用例。如果您正在编写一个库,这是一个重要的问题。让我详细说明:

考虑一个使用访问者模式的经典示例,即对某棵树的节点的操作。要添加一些细节,比如说,您刚刚为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;
    ...
    }
}

访问者模式将这个负担从客户端转移到了库中。

假设您在一个不属于自己的库中定义了一些基本内容,您需要扩展它。比如:

// In base lib:
interface ISomething {
    void DoSomething();
}

class Something1 : ISomething {
    // ...
}

class Something2 : ISomething {
    // ...
}
多态性允许您定义可以对其执行操作的新内容:

// In your lib:
class MySomething : ISomething {
}
现在,基本库可以处理您的
MySomething
,就好像它已经定义了它一样。它不允许您添加新操作
DoSomething
是我们唯一能用
ISomething
做的事情。访问者模式解决了这个问题

缺点是,使用访问者模式会消耗您定义新类型的能力,就像我们刚才展示的那样。大多数语言允许您轻松地添加操作或类型,但不能同时添加操作或类型,这一事实称为


visitor模式很酷,但除了实现编译器之外,我从未发现有必要使用它。

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

访问者模式非常有用

使用它至少有三大原因:

  • 减少在数据结构发生变化时略有不同的代码扩散

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

  • 在不更改旧代码的情况下向旧库添加信息

  • 请看一看


    干杯

    没错,该行为可以由第三方实现。好的,但是如果您定义了SQL解析器,您还没有语法定义吗?意外的用例在哪里?实际上,既然我们讨论的是解析,那么解析器生成器不应该是一个更合适的例子吗?这里你有一个任意的语法,所以你必须定义一个通用的树行者类,它不是关于SQL的结构。这是关于客户端如何使用库的结果/输出。之所以使用解析器示例,是因为它有点“经典”。顺便说一句,如果您有一个没有固定结构的完全通用API(任意“DOM”类节点),那么最好使用通用访问器方法,而不是访问者模式。您不能添加新类型,因为访问者仍然是库的一个组件,对吗?visitor类将在基本库中定义,并且将有一组固定的方法,每种类型一个。您不能在库中添加新类型,因为您不能更改访问者,也不能将访问者移动到库中,因为基本库中的类型需要在其
    accept()
    方法中引用它。