C++设计模式之访问者模式

前言

这是23+1(简单工厂模式)之中的最后一个了——访问者模式。访问者模式也是一个比较麻烦的设计模式。我也没有实战经验,对于访问者模式的理解完全来自GOF的《设计模式:可复用面向对象软件的基础》,而这篇文章就是根据对这本书的理解而写出来的。在读《设计模式:可复用面向对象软件的基础》的时候,让我想起自己做过的一个项目,该项目虽然没有使用访问者模式,但是,今天理解了该模式,如果使用该模式对之前做过的项目进行重构,将是一个不错的想法。

访问者模式

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

是基于Windows Shell开发的一个项目,在一个容器中存储了很多的Shell Items,同时定义了对Items的操作,由于项目一直都在进行后期扩展,对Items的操作在后期都需要进行扩展的;而现在的做法是,定义一个操作类,该操作类中定义了一个集合,该集合存放Items,在该操作类中扩展对应的操作方法。现在想想如果使用访问者模式也是可以的,由于Items集合是固定的,当需要扩展集合的操作时,只需要添加对应的访问者即可。

UML类图

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

使用场合

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

代码实现

代码如下:

#include <iostream>
#include <vector>
using namespace std;
 
class ConcreteElementA;
class ConcreteElementB;
 
class Visitor
{
public:
     virtual void VisitConcreteElementA(ConcreteElementA *pElementA) = 0;
     virtual void VisitConcreteElementB(ConcreteElementB *pElementB) = 0;
};
 
class ConcreteVisitor1 : public Visitor
{
public:
     void VisitConcreteElementA(ConcreteElementA *pElementA);
     void VisitConcreteElementB(ConcreteElementB *pElementB);
};
 
void ConcreteVisitor1::VisitConcreteElementA(ConcreteElementA *pElementA)
{
     // 现在根据传进来的pElementA,可以对ConcreteElementA中的element进行操作
}
 
void ConcreteVisitor1::VisitConcreteElementB(ConcreteElementB *pElementB)
{
     // 现在根据传进来的pElementB,可以对ConcreteElementB中的element进行操作
}
 
class ConcreteVisitor2 : public Visitor
{
public:
     void VisitConcreteElementA(ConcreteElementA *pElementA);
     void VisitConcreteElementB(ConcreteElementB *pElementB);
};
 
void ConcreteVisitor2::VisitConcreteElementA(ConcreteElementA *pElementA)
{
     // ...
}
 
void ConcreteVisitor2::VisitConcreteElementB(ConcreteElementB *pElementB)
{
     // ...
}
 
// Element object
class Element
{
public:
     virtual void Accept(Visitor *pVisitor) = 0;
};
 
class ConcreteElementA : public Element
{
public:
     void Accept(Visitor *pVisitor);
};
 
void ConcreteElementA::Accept(Visitor *pVisitor)
{
     pVisitor->VisitConcreteElementA(this);
}
 
class ConcreteElementB : public Element
{
public:
     void Accept(Visitor *pVisitor);
};
 
void ConcreteElementB::Accept(Visitor *pVisitor)
{
     pVisitor->VisitConcreteElementB(this);
}
 
// ObjectStructure类,能枚举它的元素,可以提供一个高层的接口以允许访问者访问它的元素
class ObjectStructure
{
public:
     void Attach(Element *pElement);
     void Detach(Element *pElement);
     void Accept(Visitor *pVisitor);
 
private:
     vector<Element *> elements;
};
 
void ObjectStructure::Attach(Element *pElement)
{
     elements.push_back(pElement);
}
 
void ObjectStructure::Detach(Element *pElement)
{
     vector<Element *>::iterator it = find(elements.begin(), elements.end(), pElement);
     if (it != elements.end())
     {
          elements.erase(it);
     }
}
 
void ObjectStructure::Accept(Visitor *pVisitor)
{
     // 为每一个element设置visitor,进行对应的操作
     for (vector<Element *>::const_iterator it = elements.begin(); it != elements.end(); ++it)
     {
          (*it)->Accept(pVisitor);
     }
}
 
int main()
{
     ObjectStructure *pObject = new ObjectStructure;
 
     ConcreteElementA *pElementA = new ConcreteElementA;
     ConcreteElementB *pElementB = new ConcreteElementB;
 
     pObject->Attach(pElementA);
     pObject->Attach(pElementB);
 
     ConcreteVisitor1 *pVisitor1 = new ConcreteVisitor1;
     ConcreteVisitor2 *pVisitor2 = new ConcreteVisitor2;
 
     pObject->Accept(pVisitor1);
     pObject->Accept(pVisitor2);
 
     if (pVisitor2) delete pVisitor2;
     if (pVisitor1) delete pVisitor1;
     if (pElementB) delete pElementB;
     if (pElementA) delete pElementA;
     if (pObject) delete pObject;
 
     return 0;
}

总结

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

设计模式中经常说的一句话是:发现变化并封装之。是否采用访问者模式,就要看“变化”是什么。访问者模式中,“变化”是具体访问者,其次是对象结构;但是,如果具体元素也会发生改变,就万万不能使用访问者模式,因为这样“牵一发而动全身”,后期的维护性就太差了。

(0)

相关推荐

  • 实例讲解iOS应用的设计模式开发中的Visitor访问者模式

    为了方便向大家展示,先给出简短的定义: 访问者模式(Visitor),表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 紧接着,给出其类结构图. 访问者模式适用于数据结构相对稳定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作结合可以相对自由地演化. 访问者模式的目的是要把处理从数据结构分离出来.很多系统可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的,

  • Python设计模式之单例模式实例

    注:使用的是Python 2.7. 一个简单实现 复制代码 代码如下: class Foo(object):    __instance = None    def __init__(self):        pass    @classmethod    def getinstance(cls):        if(cls.__instance == None):            cls.__instance = Foo()        return cls.__instance

  • 轻松掌握php设计模式之访问者模式

    访问者模式解决的问题 在我们的代码编写过程当中,经常需要对一些类似的对象添加一些的代码,我们以一个计算机对象打印组成部分为例来看下: /** * 抽象基类 */ abstract class Unit { /** *获取名称 */ abstract public function getName(); } /** * Cpu类 */ class Cpu extends Unit { public function getName() { return 'i am cpu'; } } /** *

  • JAVA设计模式之访问者模式详解

    在阎宏博士的<JAVA与模式>一书中开头是这样描述访问者(Visitor)模式的: 访问者模式是对象的行为模式.访问者模式的目的是封装一些施加于某种数据结构元素之上的操作.一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变. 分派的概念 变量被声明时的类型叫做变量的静态类型(Static Type),有些人又把静态类型叫做明显类型(Apparent Type):而变量所引用的对象的真实类型又叫做变量的实际类型(Actual Type).比如: 复制代码 代码如下: List lis

  • 详解Java设计模式编程中的访问者模式

    定义:封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作. 类型:行为类模式 类图: 例子: 例如,思考一下添加不同类型商品的购物车,当点击结算的时候,它计算出所有不同商品需付的费用.现在,计算逻辑即为计算这些不同类型商品的价格.或者说通过访问者模式我们把此逻辑转移到了另外一个类上面.让我们实现这个访问者模式的例子. 为了实现访问者模式,最先需要做的是创建能够被添加到购物车中代表不同类型商品(itemElement)的类. ItemElement

  • 学习php设计模式 php实现访问者模式(Visitor)

    访问者模式表示一个作用于某对象结构中各元素的操作.它可以在不修改各元素类的前提下定义作用于这些元素的新操作,即动态的增加具体访问者角色. 访问者模式利用了双重分派.先将访问者传入元素对象的Accept方法中,然后元素对象再将自己传入访问者,之后访问者执行元素的相应方法. 访问者模式多用在聚集类型多样的情况下.在普通的形式下必须判断每个元素是属于什么类型然后进行相应的操作,从而诞生出冗长的条件转移语句.而访问者模式则可以比较好的解决这个问题.对每个元素统一调用$element->accept($v

  • 轻松掌握python设计模式之访问者模式

    本文实例为大家分享了python访问者模式代码,供大家参考,具体内容如下 """访问者模式""" class Node(object): pass class A(Node): pass class B(Node): pass class C(A, B): pass class Visitor(object): def visit(self, node, *args, **kwargs): meth = None ""&quo

  • 详解C++设计模式编程中对访问者模式的运用

    访问者模式(visitor),表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作.访问者模式适用于数据结构相对稳定的系统.它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化.访问者模式的目的是要把处理从数据结构分离出来.很多系统可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易.反之,如果这样的系统的数据结

  • 举例讲解设计模式中的访问者模式在Java编程中的运用

    访问者(Visitor)模式:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作.访问者模式的结构图如下: 通过上图可以看到他有如下角色: 抽象访问者(Visitor)角色:定义接口,声明一个或多个访问操作. 具体访问者(ConcreteVisitor)角色:实现抽象访问者所声明的接口,也就是抽象访问者所声明的各个访问操作. 抽象元素(Visitable)角色:声明一个接受操作,接受一个访问者对象作为一个参数. 具体元素结点(Concret

  • php设计模式 Visitor 访问者模式

    复制代码 代码如下: <?php /** * 访问者模式 * * 表示一个作用于某对象结构中的各元素的操作,可以在不改变各元素的类的前提下定义作用于这些元素的新操作 * */ abstract class Visitor { abstract public function visitCroncreteElementA($element); abstract public function visitCroncreteElementB($element); } class ConcreteVis

随机推荐