在GOF的《设计模式:可复用面向对象软件的基础》一书中对访问者模式是这样说的:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。该模式的目的是要把处理从数据结构分离出来。访问者模式让增加新的操作很容易,因为增加新的操作就意味着增加一个新的访问者。访问者模式将有关的行为集中到一个访问者对象中。

 

初次接触,定义会显得晦涩并且难于理解,没关系,LZ来陪着各位一起一点一点分析定义中所提到的关键点。

先来看第一句话,说是一个作用于某对象结构中的各元素的操作,这里提到了三个事物,一个是对象结构,一个是各元素,一个是操作。那么我们可以这么理解,有这么一个操作,它是作用于一些元素之上的,而这些元素属于某一个对象结构。

好了,最关键的第二句来了,它说使用了访问者模式之后,可以让我们在不改变各元素类的前提下定义作用于这些元素的新操作。这里面的关键点在于前半句,即不改变各元素类的前提下,在这个前提下定义新操作是访问者模式精髓中的精髓。

 Visitor接口:它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素个数(Element的实现类个数)是一样的,从这点不难看出,访问者模式要求元素类的个数不能改变(不能改变的意思是说,如果元素类的个数经常改变,则说明不适合使用访问者模式)。

                  ConcreteVisitor:具体的访问者,它需要给出对每一个元素类访问时所产生的具体行为。

                  Element接口:元素接口,它定义了一个接受访问者(accept)的方法,其意义是指,每一个元素都要可以被访问者访问。

                  ConcreteElement:具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。

                  ObjectStructure:这个便是定义当中所提到的对象结构,对象结构是一个抽象表述,具体点可以理解为一个具有容器性质或者复合对象特性的类,它会含有一组元素(Element),并且可以迭代这些元素,供访问者访问。

在上面五个角色当中,最重要的就是最后一个,所谓的访问者模式,就是为了让访问者可以方便的访问对象结构而存在的。关于访问者模式的例子,很多文章和文献使用男人和女人的例子,所以LZ这里就不重复了。

  1. #include <iostream>
  2. #include <vector>
  3. #include <string>
  4. #include <algorithm>
  5. using namespace std;
  6.  
  7. class Visitor;
  8.  
  9. // Element object
  10. class Element
  11. {
  12. public:
  13. virtual void Accept(Visitor *pVisitor) = ;
  14. };
  15.  
  16. class ConcreteElementA : public Element
  17. {
  18. private:
  19. string m_name;
  20. public:
  21. ConcreteElementA();
  22. string getName(){return m_name;}
  23. void Accept(Visitor *pVisitor);
  24. };
  25.  
  26. class ConcreteElementB : public Element
  27. {
  28. private:
  29. string m_name;
  30. public:
  31. ConcreteElementB();
  32. string getName(){return m_name;}
  33. void Accept(Visitor *pVisitor);
  34. };
  35.  
  36. class Visitor
  37. {
  38. public:
  39. virtual void VisitConcreteElementA(ConcreteElementA *pElementA) = ;
  40. virtual void VisitConcreteElementB(ConcreteElementB *pElementB) = ;
  41. };
  42.  
  43. class ConcreteVisitor1 : public Visitor
  44. {
  45. public:
  46. void VisitConcreteElementA(ConcreteElementA *pElementA);
  47. void VisitConcreteElementB(ConcreteElementB *pElementB);
  48. };
  49.  
  50. void ConcreteVisitor1::VisitConcreteElementA(ConcreteElementA *pElementA)
  51. {
  52. cout << "Visitor1 vist " << pElementA->getName() << endl;
  53. }
  54.  
  55. void ConcreteVisitor1::VisitConcreteElementB(ConcreteElementB *pElementB)
  56. {
  57. cout << "Visitor1 vist " << pElementB->getName() << endl;
  58. }
  59.  
  60. class ConcreteVisitor2 : public Visitor
  61. {
  62. public:
  63. void VisitConcreteElementA(ConcreteElementA *pElementA);
  64. void VisitConcreteElementB(ConcreteElementB *pElementB);
  65. };
  66.  
  67. void ConcreteVisitor2::VisitConcreteElementA(ConcreteElementA *pElementA)
  68. {
  69. cout << "Visitor2 vist " << pElementA->getName() << endl;
  70. }
  71.  
  72. void ConcreteVisitor2::VisitConcreteElementB(ConcreteElementB *pElementB)
  73. {
  74. cout << "Visitor2 vist " << pElementB->getName() << endl;
  75. }
  76.  
  77. ConcreteElementA::ConcreteElementA()
  78. {
  79. m_name = "ConcreteElementA";
  80. }
  81.  
  82. void ConcreteElementA::Accept(Visitor *pVisitor)
  83. {
  84. pVisitor->VisitConcreteElementA(this);
  85. }
  86.  
  87. ConcreteElementB::ConcreteElementB()
  88. {
  89. m_name = "ConcreteElementB";
  90. }
  91.  
  92. void ConcreteElementB::Accept(Visitor *pVisitor)
  93. {
  94. pVisitor->VisitConcreteElementB(this);
  95. }
  96.  
  97. // ObjectStructureÀ࣬ÄÜö¾ÙËüµÄÔªËØ£¬¿ÉÒÔÌṩһ¸ö¸ß²ãµÄ½Ó¿ÚÒÔÔÊÐí·ÃÎÊÕß·ÃÎÊËüµÄÔªËØ
  98. class ObjectStructure
  99. {
  100. public:
  101. void Attach(Element *pElement);
  102. void Detach(Element *pElement);
  103. void Accept(Visitor *pVisitor);
  104.  
  105. private:
  106. vector<Element *> elements;
  107. };
  108.  
  109. void ObjectStructure::Attach(Element *pElement)
  110. {
  111. elements.push_back(pElement);
  112. }
  113.  
  114. void ObjectStructure::Detach(Element *pElement)
  115. {
  116. vector<Element *>::iterator it = find(elements.begin(), elements.end(), pElement);
  117. if (it != elements.end())
  118. {
  119. elements.erase(it);
  120. }
  121. }
  122.  
  123. void ObjectStructure::Accept(Visitor *pVisitor)
  124. {
  125. // Ϊÿһ¸öelementÉèÖÃvisitor£¬½øÐжÔÓ¦µÄ²Ù×÷
  126. for (vector<Element *>::const_iterator it = elements.begin(); it != elements.end(); ++it)
  127. {
  128. (*it)->Accept(pVisitor);
  129. }
  130. }
  131.  
  132. int main()
  133. {
  134. ObjectStructure *pObject = new ObjectStructure;
  135.  
  136. ConcreteElementA *pElementA = new ConcreteElementA;
  137. ConcreteElementB *pElementB = new ConcreteElementB;
  138.  
  139. pObject->Attach(pElementA);
  140. pObject->Attach(pElementB);
  141.  
  142. ConcreteVisitor1 *pVisitor1 = new ConcreteVisitor1;
  143. ConcreteVisitor2 *pVisitor2 = new ConcreteVisitor2;
  144.  
  145. pObject->Accept(pVisitor1);
  146. pObject->Accept(pVisitor2);
  147.  
  148. if (pVisitor2) delete pVisitor2;
  149. if (pVisitor1) delete pVisitor1;
  150. if (pElementB) delete pElementB;
  151. if (pElementA) delete pElementA;
  152. if (pObject) delete pObject;
  153.  
  154. return ;
  155. }

输出:

  1. Visitor1 vist ConcreteElementA
  2. Visitor1 vist ConcreteElementB
  3. Visitor2 vist ConcreteElementA
  4. Visitor2 vist ConcreteElementB

Visitor(访问者):为该对象结构中ConcreteElement的每一个类声明一个Visit操作。该操作的名字和特征标识了发送Visit请求给该访问者的那个类。这使得访问者可以确定正被访问元素的具体的类。这样访问者就可以通过该元素的特定接口直接访问它。
ConcreteVisitor(具体访问者):实现每个由Visitor声明的操作。每个操作实现本算法的一部分,而该算法片段乃是对应于结构中对象的类。ConcreteVisitor为该算法提供了上下文并存储它的局部状态。这一状态常常在遍历该结构的过程中累积结果。
Element(元素):定义一个Accept操作,它以一个访问者为参数。
ConcreteElement(具体元素):实现Accept操作,该操作以一个访问者为参数。
ObjectStructure(对象结构):能够枚举它的元素,同时提供一个高层的接口以允许该访问者访问它的元素。

使用场合

    1. 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作;
    2. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中;
    3. 当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作;
    4. 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好

总结

访问者模式的基本思想如下:首先拥有一个由许多对象构成的对象结构,就是上面代码中的ObjectStructure,这些对象的类都拥有一个Accept方法用来接受访问者对象;访问者是一个接口,它拥有一个Visit方法,这个方法对访问到的对象结构中不同类型的元素做出不同的操作;在对象结构的一次访问过程中,我们遍历整个对象结构,对每一个元素都实施Accept方法,在每一个元素的Accept方法中回调访问者的Visit方法,从而使访问者得以处理对象结构的每一个元素。我们就可以针对对象结构设计不同的访问者类来完成不同的操作。

Visitor 模式在不破坏类的前提下,为类提供增加新的新操作。Visitor 模式的关键是双分
派(Double-Dispatch)的技术【注释 1】。C++语言支持的是单分派。
在 Visitor 模式中 Accept()操作是一个双分派的操作。具体调用哪一个具体的 Accept
()操作,有两个决定因素:1)Element 的类型。因为 Accept()是多态的操作,需要具
体的 Element 类型的子类才可以决定到底调用哪一个 Accept()实现;2)Visitor 的类型。
Accept()操作有一个参数(Visitor* vis),要决定了实际传进来的 Visitor 的实际类别才可
以决定具体是调用哪个 VisitConcrete()实现。

[设计模式] 23 访问者模式 visitor Pattern的更多相关文章

  1. 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern)

    原文:乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) 作者:webabc ...

  2. 二十四种设计模式:访问者模式(Visitor Pattern)

    访问者模式(Visitor Pattern) 介绍表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 示例有一个Message实体类,某些对象对 ...

  3. 访问者模式(Visitor Pattern)——操作复杂对象结构

    模式概述 在软件开发中,可能会遇到操作复杂对象结构的场景,在该对象结构中存储了多个不同类型的对象信息,而且对同一对象结构中的元素的操作方式并不唯一,可能需要提供多种不同的处理方式,还有可能增加新的处理 ...

  4. C#设计模式——访问者模式(Visitor Pattern)

    一.概述由于需求的改变,某些类常常需要增加新的功能,但由于种种原因这些类层次必须保持稳定,不允许开发人员随意修改.对此,访问者模式可以在不更改类层次结构的前提下透明的为各个类动态添加新的功能.二.访问 ...

  5. 十一个行为模式之访问者模式(Visitor Pattern)

    定义: 提供一个作用于某对象结构(通常是一个对象集合)的操作的接口,使得在添加新的操作或者在添加新的元素时,不需要修改原有系统,就可以对各个对象进行操作. 结构图: Visitor:抽象访问者类,对元 ...

  6. 设计模式(17) 访问者模式(VISITOR) C++实现

    意图: 表示一个作用于某对象结构的各元素的操作.它使你可以再不改变各元素的类的前提下定义作用于这些元素的新操作. 动机: 之前在学校的最后一个小项目就是做一个编译器,当时使用的就是访问者模式. 在静态 ...

  7. 【设计模式】—— 访问者模式Visitor

    前言:[模式总览]——————————by xingoo 模式意图 对于某个对象或者一组对象,不同的访问者,产生的结果不同,执行操作也不同.此时,就是访问者模式的典型应用了. 应用场景 1 不同的子类 ...

  8. 行为型设计模式之访问者模式(Visitor)

    结构 意图 表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 适用性 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依 ...

  9. 访问者模式-Visitor Pattern

    1.主要优点 访问者模式的主要优点如下: (1) 增加新的访问操作很方便.使用访问者模式,增加新的访问操作就意味着增加一个新的具体访问者类,实现简单,无须修改源代码,符合“开闭原则”. (2) 将有关 ...

随机推荐

  1. IOS 模仿TableView封装

    一.先贴一下未封装的代号,好跟后面的对比 @interface MTHomeDropdown : UIView + (instancetype)dropdown; @property (nonatom ...

  2. (转)Yale CAS + .net Client 实现 SSO(5)

    第一部分:安装配置 Tomcat 第二部分:安装配置 CAS 第三部分:实现 ASP.NET WebForm Client 第四部分:实现基于数据库的身份验证 第五部分:扩展基于数据库的身份验证 1. ...

  3. ThinkPHP中的内置标签

    ThinkPHP中的内置标签 1.内置标签分类 闭合标签 <tag></tag> 开放标签 <tag /> 2.包含文件标签 主要功能:实现对文件的包含(类似于re ...

  4. AOJ 2200 Mr. Rito Post Office

    Mr. Rito Post Office Time Limit : 8 sec, Memory Limit : 65536 KB Problem D: Mr. Rito Post Office あなた ...

  5. 解決 imagick 在 多线程运行时导致CPU暴增到100%的方法

    假如把imagic 安装到 /usr/local/imagemagick 目录 首先用/usr/local/imagemagick/bin/convert -version指令查看一下输出內容是否已经 ...

  6. 如何在Ubuntu下使用TF/SD 卡制作Exynos 4412 u-boot启动盘

    /** ****************************************************************************** * @author    Maox ...

  7. mount.nfs: access denied by server while mounting localhost:/home/xuwq/minilinux/system

    在执行命令如下: mount -t nfs localhost:/home/xuwq/minilinux/system /mnt 出现的错误: mount.nfs: access denied by ...

  8. Flex-box 学习

    .flex-cont{ /*定义为flexbox的“父元素”*/ display: -webkit-box; display: -webkit-flex; display: flex; /*子元素沿主 ...

  9. php win主机下实现ISAPI_Rewrite伪静态

    有的win主机iss不支持 .htaccess 文件, 我在这里指的不是本地 在本地的话用apmserv服务器可以用.htaccess 文件,用apmserv服务器环境配置伪静态可以看 php 伪静态 ...

  10. WCF全面解析第二章 地址(Adress)

    2.1 统一资源标识(URL) 2.1.1 Http/Https 2.1.2 Net.TCP 2.1.3 Net.Pipe WCF只将命名管道专门用于同一台机器的跨进程通信. 2.1.4 Net.Ms ...