[设计模式] 23 访问者模式 visitor Pattern

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

 

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

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

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

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

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

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

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

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

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

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

class Visitor;

// Element object
class Element
{
    public:
        virtual void Accept(Visitor *pVisitor) = 0;
};

class ConcreteElementA : public Element
{
    private:
        string m_name;
    public:
        ConcreteElementA();
        string getName(){return m_name;}
        void Accept(Visitor *pVisitor);
};

class ConcreteElementB : public Element
{
    private:
        string m_name;
    public:
        ConcreteElementB();
        string getName(){return m_name;}
        void Accept(Visitor *pVisitor);
};

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)
{
    cout << "Visitor1 vist " << pElementA->getName() << endl;
}

void ConcreteVisitor1::VisitConcreteElementB(ConcreteElementB *pElementB)
{
    cout << "Visitor1 vist " << pElementB->getName() << endl;
}

class ConcreteVisitor2 : public Visitor
{
    public:
        void VisitConcreteElementA(ConcreteElementA *pElementA);
        void VisitConcreteElementB(ConcreteElementB *pElementB);
};

void ConcreteVisitor2::VisitConcreteElementA(ConcreteElementA *pElementA)
{
    cout << "Visitor2 vist " << pElementA->getName() << endl;
}

void ConcreteVisitor2::VisitConcreteElementB(ConcreteElementB *pElementB)
{
    cout << "Visitor2 vist " << pElementB->getName() << endl;
}

ConcreteElementA::ConcreteElementA()
{
    m_name = "ConcreteElementA";
}

void ConcreteElementA::Accept(Visitor *pVisitor)
{
    pVisitor->VisitConcreteElementA(this);
}

ConcreteElementB::ConcreteElementB()
{
    m_name = "ConcreteElementB";
}

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;
}

输出:

Visitor1 vist ConcreteElementA
Visitor1 vist ConcreteElementB
Visitor2 vist ConcreteElementA
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()实现。

时间: 2024-10-13 16:16:59

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

设计模式之访问者模式(Visitor)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

二十三种设计模式[23] - 访问者模式(Visitor Pattern)

前言 访问者模式,是一种将数据的结构与其操作分离的类行为型模式.它能够帮助我们解决数据结构稳定但数据操作多变的问题,使我们可以很容易的增加或修改数据的操作. 在<设计模式 - 可复用的面向对象软件>一书中将之描述为" 表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作 ". 结构 Visitor(访问者接口):定义了每种元素的访问行为,一般情况下访问行为的个数与元素种类的个数一致: ConcretVisitor(具体访问

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

意图: 表示一个作用于某对象结构的各元素的操作.它使你可以再不改变各元素的类的前提下定义作用于这些元素的新操作. 动机: 之前在学校的最后一个小项目就是做一个编译器,当时使用的就是访问者模式. 在静态分析阶段,将源程序表示为一个抽象语法树,编译器需要在抽象语法树的基础上实施某些操作以进行静态语义分析.可能需要定义许多操作以进行类型检查.代码优化.流程分析.检查变量是否在使用前被赋值,等等. 这个需求的特点是:要求对不同的节点进行不同的处理. 常规设计方法:不同的节点封装不同的操作. 缺点是,节点

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

对于某个对象或者一组对象,不同的访问者,产生的结果不同,执行操作也不同.此时,就是访问者模式的典型应用了. 应用场景 1 不同的子类,依赖于不同的其他对象 2 需要对一组对象,进行许多不相关的操作,又不想在类中是现在这些方法 3 定义的类很少改变,但是执行的操作却经常发生改变. 回到顶部 模式结构 Context 环境角色 class Context{ List<Node> list = new ArrayList(); public void add(Node node) { list.ad

访问者模式 Visitor Pattern

访问者模式: 闻词达意,肯定有访问者(对象).接受者(对象).写个实例: 1.IVisitor 抽象访问者角色,声明一个访问操作接口visit(Acceptor ). 2.Visitor 具体访问者角色,实现IVisitor声明的接口. 3.Acceptor 定义一个被访问者,定义接受访问操作(accept(IVisitor)). 4.AcceptorInstance 被访问者实例,实现接受访问操作,然后同意(回调)访问者访问自己. 1 package headfirst.visitor; 2

设计模式:访问者(Visitor)模式

设计模式:访问者(Visitor)模式 一.前言    什么叫做访问,如果大家学过数据结构,对于这点就很清晰了,遍历就是访问的一般形式,单独读取一个元素进行相应的处理也叫作访问,读取到想要查看的内容+对其进行处理就叫做访问,那么我们平常是怎么访问的,基本上就是直接拿着需要访问的地址(引用)来读写内存就可以了.    为什么还要有一个访问者模式呢,这就要放到OOP之中了,在面向对象编程的思想中,我们使用类来组织属性,以及对属性的操作,那么我们理所当然的将访问操作放到了类的内部,这样看起来没问题,但

设计模式入门之访问者模式Visitor

Set集合的配置 数据表的创建:表关系一个员工拥有多个身份 create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20) default NULL, salary INT default NULL, PRIMARY KEY (id) ); create table CERTIFICATE ( id INT NOT NULL aut

访问者模式 Visitor 行为型 设计模式(二十七)

访问者模式 Visitor <侠客行>是当代作家金庸创作的长篇武侠小说,新版电视剧<侠客行>中,开篇有一段独白: “茫茫海外,传说有座侠客岛,岛上赏善罚恶二使,每隔十年必到中原武林,向各大门派下发放赏善罚恶令, 强邀掌门人赴岛喝腊八粥,拒接令者,皆造屠戮,无一幸免,接令而去者,杳无音讯,生死未仆,侠客岛之行,已被视为死亡之旅.” 不过话说电视剧,我总是觉得老版的好看. 意图 表示一个作用于某对象结构中的各元素的操作. 它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作.

设计模式 - 装饰者模式(Decorator Pattern) Java的IO类 使用方法

装饰者模式(Decorator Pattern) Java的IO类 使用方法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26716823 装饰者模式(decorator pattern)参见: http://blog.csdn.net/caroline_wendy/article/details/26707033 Java的IO类使用装饰者模式进行扩展, 其中FilterInputStream类, 就是装饰者(decora