zoukankan      html  css  js  c++  java
  • 设计模式之访问者模式

    访问者模式

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

    访问者模式UML类图

    Visitor:为该对象结构中ConcreteElement的每一个具体类声明一个Visit操作。它的参数就是可以访问的元素,它的方法个数理论上来说是和ConcreteElement具体子类的个数是一致的。因此访问者模式要求元素(Element)的类组要稳定,如果要经常添加、移除元素类,那么必然会导致频繁的修改Visitor接口,这不适合访问者模式。

    ConcreteVisitor:具体的访问者,实现由每个Visitor声明的操作。每个操作实现算法的一部分,而该算法片段乃是对应于结构中对象的类。(就是要给出对每一个ConcreteElement类进行访问时所要产生的具体行为)。

    Element:定义一个Accept操作,它以一个访问为参数。意思是每一个元素都要可以被访问者访问。

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

    ObjectStructure:能枚举它的元素,可以提供一个高层接口以允许访问者访问它的元素,(这个就是模式定义中和Visitor类解释中提到的对象结构)。对象结构是一个抽象表述,具体点可以理解为一个具有容器性质或者复合对象特性的类,它会含有一组元素(Element),并可以迭代,以便访问者访问。

      在上面的5个角色中,最重要的就是ObjectStructure,所谓访问者模式就是为了访问者可以方便的访问对象结构而存在的。所谓的状态,其实就是访问者根据对象可能有的某个状态做出不同的反应来实现的。

    访问者模式的优缺点

    优点:

      1.增加新的访问操作很方便,使用访问者模式,增加新的访问操作,实际上就是增加一个新的具体访问类,实现不需要修改已有的代码,符合开闭原则。

      2.将有关元素对象的访问行为集中在了一个访问者类中,而不是分散在各个不同的元素类里,类的职责更加清晰,有利于对象结构中元素对象的复用。相同的对象结构,可以供多个不同的访问者访问。

      3.让用户能够在不修改现有元素类层次结构的情况,定义作用于该层次结构的操作。

    缺点:

      1.增加新的元素类困难,每新增一个元素类,都意味着要修改Visitor类,并且需要在每一个具体访问者类中增加具体的访问实现,违反了开放-封闭原则。(如果对象结构不稳定的话,说明是不适用访问者模式的)

      2.破坏了封装

    适用场景:

      1.访问者模式适用于数据结构相对稳定的系统。它把数据结构和作用于结构之上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。使用访问者模式的目的是要把处理从数据结构中分离出来。很多系统可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得算法的操作变的更加容易。反之,如果这样的系统的数据结构对象易于变化,经常要有新的数据对象增加进来,那么是不适合使用访问者模式的。

      2.ConcreteVisitor通常可以单独开发,不必跟ConcreteElement写在一起。这样就提高了ConcreteElement的独立性,如果把访问操作设计为ConcreteElement的方法,那么每次新增操作都要修改ConcreteElement的源码,这就造成了数据结构和处理方法之间的紧耦合。

    代码示例

      案例来源于《大话设计模式》的男人和女人的例子,本文中使用C++在linux下实现。 在这里例子中Element就是人,人只分为男人和女人,所以ConcreteElement就只有男人类和女人类两种,因此对于人这个元素来说,它的对象结构是稳定的。人的一生有很多状态,例如成功/失败,结婚/不结婚,恋爱/结婚等等。对每一种状态,男人和女人的反应是不一样的,访问者类Visitor的作用就是根据不同的状态得到男人和女人的不同反应,如果有新的状态,那么就增加一个新的访问者类即可。男人和女人类都需要提供一个accept方法,参数是一个访问者,通常是使用访问者提供的访问该元素类的方法,得到男人和女人在面对某一状态下的反应。对象结构,需要维护一个Element的列表,这个列表是可以迭代的,以方便访问者访问不同Element实例。

    1.Element和Visitor类

    #ifndef VISITOR_H_
    #define VISITOR_H_
    //Action类就是访问者模式中的Visitor类。之所以起名就做Action,是因为它根据不同的状态对元素采用了不同的操作
    //具体元素类只分两种,男人类和女人类,访问者需要提供针对这种类型的访问操作
    //所以Action类根本上是一个状态类
    class Man;
    class Woman;
    #include <iostream>
    #include <typeinfo>
    
    class Action
    {
    public:
        virtual void visitMan(Man ConcreteElementA) = 0;    //访问男人类(实际上就是根据状态的不同得到男人的反应)
        virtual void visitWoman(Woman ConcreteElementB) = 0;    //访问女人类(根据状态的不同得到女人的反应)
        Action() = default;
        virtual ~Action() = default;
    };
    
    //Human就相当于是访问者模式中的Element,
    class Human
    {
    public:
        virtual void accept(Action *visitor) = 0;
        Human() = default;
        virtual ~Human() = default;
    };
    #endif
    Visitor&Elment

    2.ConcreteElement类

    #ifndef MAN_H_
    #define MAN_H_
    
    #include "VisitorPattern.h"
    
    class Man:public Human
    {
    public:
        void accept(Action *visitor) override;
        Man() = default;
        ~Man() = default;
    };
    #endif
    
    #include "Man.h"
    
    void Man::accept(Action *visitor)
    {
        visitor->visitMan(*this);
    }
    
    #ifndef WOMAN_H_
    #define WOMAN_H_
    
    #include "VisitorPattern.h"
    
    class Woman: public Human
    {
    public:
        void accept(Action *visitor) override;
        Woman() = default;
        ~Woman() = default;
    };
    #endif
    
    #include "Woman.h"
    
    void Woman::accept(Action *visitor)
    {
        visitor->visitWoman(*this);
    }
    ConcreteElement

    3.ConcreteVisitor类

    #ifndef SUCCESS_H_
    #define SUCCESS_H_
    
    #include "Man.h"
    #include "Woman.h"
    
    class Success : public Action
    {
    public:
        void visitMan(Man concreteElementA) override;
        void visitWoman(Woman concreteElementB) override;
        Success() = default;
        ~Success() = default;
    };
    #endif
    
    #include "Success.h"
    
    void Success::visitMan(Man concreteElementA)
    {
        std::cout << typeid(concreteElementA).name() << typeid(*this).name() << "shi,bei hou duo ban you yi ge wei da de nv ren." << std::endl;
    }
    
    void Success::visitWoman(Woman concreteElementB)
    {
        std::cout << typeid(concreteElementB).name() << typeid(*this).name() << "shi,beihou da duo you yi ge bu cheng gong de nan ren." << std::endl;
    }
    
    #ifndef FAILED_H_
    #define FAILED_H_
    
    #include "VisitorPattern.h"
    #include "Man.h"
    #include "Woman.h"
    
    class Failed :public Action
    {
    public:
        void visitMan(Man concreteElementA) override;
        void visitWoman(Woman concreteElementB) override;
        Failed() = default;
        ~Failed() = default;
    };
    #endif
    
    #include "Failed.h"
    
    void Failed::visitMan(Man concreteElementA)
    {
        std::cout << typeid(concreteElementA).name() << typeid(*this).name() << "shi,men tou he jiu,shui ye bu yong quan." << std::endl;
    }
    
    void Failed::visitWoman(Woman concreteElementB)
    {
        std::cout << typeid(concreteElementB).name() << typeid(*this).name() << "shi,yan lei wang wang,shui ye quan bu liao." << std::endl;
    }
    ConcreteVisitor

    4.ObjectStructure类

    #ifndef OBJECTSTRUCTURE_H_
    #define OBJECTSTRUCTURE_H_
    
    //对象结构
    
    #include "VisitorPattern.h"
    #include <list>
    
    class ObjectStructure
    {
    private:
        std::list<Human *> elements;
    public:
        //增加
        void add(Human *objHuman);
        //移除
        void remove(Human *objHuman);
        //查看显示
        void display(Action *visitor);
        ObjectStructure() = default;
        ~ObjectStructure() = default;
    };
    #endif
    
    #include "ObjectStructure.h"
    
    void ObjectStructure::add(Human *objHuman)
    {
       elements.push_back(objHuman);
    }
    
    void ObjectStructure::remove(Human *objHuman)
    {
        elements.remove(objHuman);
    }
    
    void ObjectStructure::display(Action *visitor) 
    {
        for(auto value : elements)
        {
        value->accept(visitor);
        }
    }
    ObjectStructure

    5.Client

    #include "ObjectStructure.h"
    #include "Man.h"
    #include "Woman.h"
    #include "Success.h"
    #include "Failed.h"
    
    using namespace std;
    
    int main(int argc,char *argv[])
    {
        ObjectStructure objObjectStructure;
        Man objMan;
        Woman objWoman;
        objObjectStructure.add(&objMan);
        objObjectStructure.add(&objWoman);
        
        //成功时的反应
        Success objSuccess;
        objObjectStructure.display(&objSuccess);
        //失败时的反应
        Failed objFailed;
        objObjectStructure.display(&objFailed);
        
        return (1);
    }
    Client
  • 相关阅读:
    运维人员常用的linux命令汇总
    Linux 入侵痕迹清理技巧
    xshell突出显示
    Linux终端显示中文
    mysql8.0设uuid函数为默认值
    Linux使用NFS作为文件共享目录服务
    修改docker运行容器的映射端口
    Apache-kafka以及zookeepeer单机安装
    kafka-confluent管控中心安装
    记CentOS8下安装Docker
  • 原文地址:https://www.cnblogs.com/ToBeExpert/p/9704512.html
Copyright © 2011-2022 走看看