• 22 行为型模式-----访问者模式


    模式动机(Visitor Pattern)访问者模式用于操作存储于某个集合中的各元素,使得可以在不改变元素类的前提下定义作用于这些元素的新操作。

    之所以使用访问者类,是因为存储于某个集合中的元素可能具有不同的特性,而不同的访问者可能更看重某一方面的特性,如果让集合类本身承担访问操作,那么对于不同的访问操作,必须对应地定义不同的方法,不仅使得类变得极其庞大,而且难以扩展。

    利用访问者模式可以解决上述问题。其将集合中元素的操作封装在一个Visitor继承体系中,不同的Visitor实现类可以满足不同的访问需求,增加新的访问方式只需添加一个ConcreteVisitor,系统中负责存储数据的集合类不需要做丝毫的变动。因此访问者模式的核心就是:在不改变存储元素类的前提下定义作用于这些元素的新操作。

     

    参与者:

    Visitor: 定义了访问不同特性元素的接口。

    ConcreteVisitor: 实现了对于特定元素的访问操作。

    Element: 定义了一个接受访问者的Accept操作接口。

    ConcreteElement: 实现了Accept操作,根据不同的元素类型及其访问者类型实现访问操作。

    ObjectStructure: 存储不同类型的元素,对外提供增加和删除元素的接口,以及接受一个访问者对象来访问存储的元素。

     

    合作:

    一个使用Visitor模式的客户必须创建一个ConcreteVisitor对象,然后遍历存储元素的结构对象ObjectStructure,并用指定的访问者访问这些元素;当一个具体的元素被访问时,它会调用与自身类型相关的Visitor操作。

     

    模式结构图:

     

    模式代码:

    bt_访问者模式.h:

     1 #ifndef VP_H
     2 #define VP_H
     3 #include <iostream>
     4 #include <list>
     5 using namespace std;
     6 
     7 /*
     8     抽象元素
     9 */
    10 class Visitor;
    11 class Element
    12 {
    13 public:
    14     virtual ~Element() = 0;
    15     virtual void Accept(Visitor* v) = 0;
    16 };
    17 
    18 /*
    19     具体元素
    20 */
    21 class ConcreteElementA : public Element
    22 {
    23 public:
    24     void Accept(Visitor* v);
    25     void OperationA();
    26 };
    27 class ConcreteElementB : public Element
    28 {
    29 public:
    30     void Accept(Visitor* v);
    31     void OperationB();
    32 };
    33 
    34 /*
    35     抽象访问者
    36 */
    37 class Visitor
    38 {
    39 public:
    40     virtual ~Visitor() = 0;
    41     virtual void VisitConcreteElementA(ConcreteElementA* pEA) = 0;
    42     virtual void VisitConcreteElementB(ConcreteElementB* pEB) = 0;
    43 };
    44 
    45 /*
    46     具体访问者
    47 */
    48 class ConcreteVisitor1 : public Visitor
    49 {
    50 public:
    51     virtual void VisitConcreteElementA(ConcreteElementA* pEA);
    52     virtual void VisitConcreteElementB(ConcreteElementB* pEB);
    53 };
    54 class ConcreteVisitor2 : public Visitor
    55 {
    56 public:
    57     virtual void VisitConcreteElementA(ConcreteElementA* pEA);
    58     virtual void VisitConcreteElementB(ConcreteElementB* pEB);
    59 };
    60 
    61 /*
    62     对象结构
    63 */
    64 class ObjectStructure
    65 {
    66 public:
    67     ObjectStructure();
    68     ~ObjectStructure();
    69     void AddElement(Element* elem);
    70     void RemoveElement(Element* elem);
    71     void Accept(Visitor* v);
    72 
    73 private:
    74     list<Element*>* elements;
    75 };
    76 #endif // VP_H

    bt_访问者模式.cpp:

     1 #include "bt_访问者模式.h"
     2 
     3 /* 抽象元素 */
     4 Element::~Element(){  }
     5 
     6 /* 具体元素 */
     7 void ConcreteElementA::Accept(Visitor* v){ v->VisitConcreteElementA(this); }
     8 void ConcreteElementA::OperationA(){ cout << "Operation A" << endl; }
     9 void ConcreteElementB::Accept(Visitor* v){ v->VisitConcreteElementB(this); }
    10 void ConcreteElementB::OperationB(){ cout << "Operation B" << endl; }
    11 
    12 
    13 /* 抽象访问者 */
    14 Visitor::~Visitor(){  }
    15 
    16 /* 具体访问者 */
    17 void ConcreteVisitor1::VisitConcreteElementA(ConcreteElementA* pEA)
    18 {
    19     cout << "Visitor1 visits ConcreteElementA" << endl;
    20     pEA->OperationA();
    21 }
    22 void ConcreteVisitor1::VisitConcreteElementB(ConcreteElementB* pEB)
    23 {
    24     cout << "Visitor1 visits ConcreteElementB" << endl;
    25     pEB->OperationB();
    26 }
    27 void ConcreteVisitor2::VisitConcreteElementA(ConcreteElementA* pEA)
    28 {
    29     cout << "Visitor2 visits ConcreteElementA" << endl;
    30     pEA->OperationA();
    31 }
    32 void ConcreteVisitor2::VisitConcreteElementB(ConcreteElementB* pEB)
    33 {
    34     cout << "Visitor2 visits ConcreteElementB" << endl;
    35     pEB->OperationB();
    36 }
    37 
    38 /* 对象结构 */
    39 ObjectStructure::ObjectStructure(){ elements = new list<Element*>; }
    40 ObjectStructure::~ObjectStructure(){ delete elements; }
    41 
    42 void ObjectStructure::AddElement(Element* elem){ elements->push_back(elem); }
    43 void ObjectStructure::RemoveElement(Element* elem){ elements->remove(elem); }
    44 
    45 void ObjectStructure::Accept(Visitor* v)
    46 {
    47     list<Element*>::iterator iter;
    48     for(iter = elements->begin(); iter != elements->end(); iter++)
    49     {
    50         (*iter)->Accept(v);
    51     }
    52 }

    测试用例.cpp:

     1 #include "bt_访问者模式.h"
     2 
     3 int main()
     4 {
     5     cout << "***** 访问者模式测试 *****" << endl;
     6     ObjectStructure* obj = new ObjectStructure;
     7     Element* elementA1 = new ConcreteElementA;
     8     Element* elementA2 = new ConcreteElementA;
     9     Element* elementB1 = new ConcreteElementB;
    10     Element* elementB2 = new ConcreteElementB;
    11 
    12     obj->AddElement(elementA1);
    13     obj->AddElement(elementA2);
    14     obj->AddElement(elementB1);
    15     obj->AddElement(elementB2);
    16 
    17     Visitor* visitor1 = new ConcreteVisitor1;
    18     Visitor* visitor2 = new ConcreteVisitor2;
    19 
    20     obj->Accept(visitor1);
    21     cout << endl;
    22     obj->Accept(visitor2);
    23 
    24     return 0;
    25 }
    
    

     

    模式分析:

    :: 访问者模式使得在不改变元素类的情况下可以增加新的操作,其使用了一种”Double Dispatch”模式,即具体访问操作依赖于具体的Element类型和Visitor类型两个方面,如上程序中:obj->Accept(visitor1) / obj->Accept(visitor2),其中用到了visitor1和visitor2两种访问者类型,而且obj中又存放了两种类型的元素ConcreteElementA / ConcreteElementB。

    :: 对象结构的遍历操作由谁负责?

    若由对象结构自身负责遍历,则只需要对其包含的集合对象调用Accept()操作即可,我们通常使用这种方式;若由访问者负责遍历,那么意味着每一个具体的ConcreteVisitor都必须负责每一个具体的ConcreteElement的访问,代码复用性太差,难以维护;若由外部迭代器负责遍历,则只能使用元素类型参数,无法使用访问者类型参数,因此无法实现”Double Dispatch”。

    :: 如果元素对象本身有多种,而且需要对于每种元素执行不同的操作,那么适合使用Visitor模式,这就需要在设计ConcreteVisitor时,只关注与特定类型元素相关的访问操作即可,没必要关注其他类型元素。当然了,此时抽象的Visitor中必须给出关于其他类型访问的默认实现,而不能再定义为纯虚函数。

    :: 如果对象结构类很少改变,但需要经常在该结构上定义新操作,如果此时不使用Visitor模式,那么改变对象结构类时,必须重新定义所有访问者的接口,代价很大。

     

    模式优缺点:

    1>  Visitor模式使得新增访问操作变得非常容易,只需要增加一个ConcreteVisitor类即可。

    2>  与迭代器相比,访问者模式可以访问具有不同类型元素的对象结构;而迭代器只能对同一类型的对象进行操作。

    3>  增加新的ConcreteElement类很困难,必须在Visitor接口中添加对应的访问操作,同时,如果Visitor中不给出缺省的实现,那么还必须修改每一个ConcreteVisitor实现类。

    4>  访问者模式要求ConcreteElement类提供的功能完全满足访问者的需求,这意味着可能需要提供访问元素内部状态的公共操作,因此会破坏封装性。

     

     

     

  • 相关阅读:
    python基础--(hashlib,configparser,logging)模块功能
    java发送邮件
    Struts2和SpringMVC的action是单例还是原型的?
    HashSet存储过程中如何排除不同的自定义对象?
    使用win32Diskimager后恢复U盘(合并U盘容量)
    linux进程与端口
    centos 7.6 忘记root密码
    Authentication token is no longer valid; new one required You (oracle) are not allowed to access to (crontab) because of pam configuration.
    存储过程
    oracle extract()函数
  • 原文地址:https://www.cnblogs.com/benxintuzi/p/4580312.html
Copyright © 2020-2023  润新知