zoukankan      html  css  js  c++  java
  • 访问者模式(Visitor)

    访问者模式(Visitor

     

    访问者模式(Visitor

    意图:表示一个作用于某对象结构中的各元素的操作,它使你在不改变各元素的类的前提下定义作用于这些元素的新操作。

    应用:作用于编译器语法树的语义分析算法。

    模式结构

     

    心得

    访问者模式是要解决对对象添加新的操作和功能时候,如何尽可能不修改对象的类的一种方法。一般为对象添加功能,是需要向对象添加成员函数。但这里对对象(ConcreteElement)添加了一个统一的接口——accept,来接收一个访问者对象。如何把对对象的操作移出到类外,正是接收参数(Visitor)的作用。它通过调用Visitor的接口函数visitConcreteElement针对当前对象进行操作,当然,当前对象的指针需要被作为参数传递出去,以便对对象状态进行访问。这样,拥有Element集合的对象ObjectStruct只要通过遍历操作,每次调用对象的accept接口就可以让对象自动告诉访问者使用执行什么样的功能了。当需要为对象扩展功能时,只需要再添加一个访问者,重定义对每类对象进行访问的方式就可以了。这里涉及一个双向分派的概念,即accept操作的调用者(Element)是运行时多态的,而且参数Visitor也是运行时多态的。正是因为如此,才让用户可以通过将新添的功能封装为对象,来实现对对象集合批量的不同操作。

    举例

    这里其实可以把Element想象为编译器的抽象语法树节点,ConcreteElement可以看作具体的树节点,如赋值语句和变量访问节点。Visitor就可以看作语义分析阶段的语义检查,ConcreteVistor可以看作类型检查功能和代码生成功能。这些语义分析的功能显然不应该和语法树放在一起,那么把它封装为访问者,让他们为不同的节点生成单独的分析流程和算法。再在节点对象内部使用统一接口accept调用对应的算法即可,节点内容通过自身的对象指针传递给访问者对象。按照图中所示关系,我们给出C++代码如下:

     
    //元素基类
    class Visitor;
    class Element
    {
    public:
        virtual void accept(Visitor*)=0;
        virtual ~Element(){}
    };
    //访问者基类
    class ConcreteElementA;
    class ConcreteElementB;
    class Visitor
    {
    public:
        virtual void visitConcreteElementA(ConcreteElementA*)=0;
        virtual void visitConcreteElementB(ConcreteElementB*)=0;
        virtual ~Visitor(){}
    };
    //具体元素
    class ConcreteElementA:public Element
    {
    public:
        virtual void accept(Visitor*v)
        {
            v->visitConcreteElementA(this);//双向分派
        }
        void operationA()
        {
            cout<<"对元素A的处理"<<endl;
        }
    };
    class ConcreteElementB:public Element
    {
    public:
        virtual void accept(Visitor*v)
        {
            v->visitConcreteElementB(this);
        }
        void operationB()
        {
            cout<<"对元素B的处理"<<endl;
        }
    };
    //具体的访问者
    class ConcreteVisitor1:public Visitor
    {
    public:
        virtual void visitConcreteElementA(ConcreteElementA*ea)
        {
            cout<<"访问者1";
            ea->operationA();
        }
        virtual void visitConcreteElementB(ConcreteElementB*eb)
        {
            cout<<"访问者1";
            eb->operationB();
        }
    };
    class ConcreteVisitor2:public Visitor
    {
    public:
        virtual void visitConcreteElementA(ConcreteElementA*ea)
        {
            cout<<"访问者2";
            ea->operationA();
        }
        virtual void visitConcreteElementB(ConcreteElementB*eb)
        {
            cout<<"访问者2";
            eb->operationB();
        }
    };
    //管理和遍历元素集合的高层类
    class ObjectStruct
    {
        list<Element*>data;
    public:
        void addElement(Element*e)
        {
            data.push_back(e);
        }
        void delElement(Element*e)
        {
            data.remove(e);
        }
        void dispaly(Visitor*v)
        {
            for(list<Element*>::iterator it=data.begin();
                it!=data.end();++it)
            {
                (*it)->accept(v);
            }
        }
        ~ObjectStruct()
        {
            for(list<Element*>::iterator it=data.begin();
                it!=data.end();++it)
            {
                delete (*it);
            }
        }
    };
     

    这里需要实现一下ObjectStruct类,因为它提供的对象集合的高层遍历。用户对元素的逐个操作被简化为如下方式:

     
    ObjectStruct os;//初始化集合
    os.addElement(new ConcreteElementA());
    os.addElement(new ConcreteElementB());
    os.addElement(new ConcreteElementB());
    os.addElement(new ConcreteElementA());
    Visitor*v1=new ConcreteVisitor1();//创建访问者1
    Visitor*v2=new ConcreteVisitor2();
    os.dispaly(v1);//用访问者1对元素进行操作【双向分派】
    os.dispaly(v2);
     

    由此看来,只要对象的继承结构(数据结构)变化不大的情况下,比如不会添加新的类型的节点,使用Visitor模式是非常合适的。用户只要按需创建合适的访问者类实现之,然后遍历集合对象,直接“访问”就可以了。额外需要说明的一点是,访问者并不一定让具体元素类继承于统一的父类,从访问者抽象类也能看出,抽象类接口仅仅依赖于具体实现的类。之所以让它们具有公共的基类主要是还是为了批量操作的方便,即使没有继承统一的基类,访问者模式依然能工作,也能为具体的类添加功能。

    来源:http://www.cnblogs.com/fanzhidongyzby/archive/2012/12/14/2818809.html

  • 相关阅读:
    iText + Freemarker实现pdf的导出,支持中文、css以及图片,页眉页脚,页眉添加图片
    SpringBoot使用拦截器、过滤器、监听器
    Java中boolean类型占用多少个字节?我说一个,面试官让我回家等通知
    【搞定面试官】try中有return,finally还会执行吗?
    细说JVM内存模型
    easyExcel简介#
    gbdt和xgboost api
    RNN BPTT
    tensorflow LSTM
    [转]python与numpy基础
  • 原文地址:https://www.cnblogs.com/94julia/p/3092230.html
Copyright © 2011-2022 走看看