一:概念

访问者模式,是行为模式之一,它分离对象的数据和行为,使用Visitor模式,可以不修改已有类的情况下,增加新的操作角色和职责。

二:动机

在软件构建的过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法)。如果直接在类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计
如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作,从而避免上述问题?

三:代码讲解

(一)原代码

#include <iostream>
using namespace std; class Visitor; class Element
{
public:
virtual void Func1() = ; virtual ~Element(){}
}; class ElementA : public Element
{
public:
void Func1() override{
//...
}
}; class ElementB : public Element
{
public:
void Func1() override{
//***
}
};

需求:想要增加一个新的功能

#include <iostream>
using namespace std; class Visitor; class Element
{
public:
virtual void Func1() = ; virtual void Func2(int data)=0;  //定义虚方法,后面继承父类的都要一一实现
virtual void Func3(int data)=;
//... virtual ~Element(){}
}; class ElementA : public Element
{
public:
void Func1() override{
//...
} void Func2(int data) override{
//...
}
}; class ElementB : public Element
{
public:
void Func1() override{
//***
} void Func2(int data) override {
//***
}
};
这不是一个好的方法,在部署后,我们再次修改,此时的代价是十分高的,违背了开闭原则

(二)visitor模式---->前提:能够预料到未来可能会为这整个类层次结构添加新的操作,但是我不知道要加多少操作,什么操作

1.预先设计Element基类

class Visitor;

class Element
{
public:
virtual void accept(Visitor& visitor) = 0; //第一次多态辨析
  //accept方法为将来埋下伏笔,将来要添加新的功能,加到这里去找他,进行绑定visitor
virtual ~Element(){}
};

2.完善visitor基类

class Visitor{
public:
virtual void visitElementA(ElementA& element) = 0;  //针对每一个子类生成一个方法对应
virtual void visitElementB(ElementB& element) = 0;
virtual ~Visitor(){}
};

3.element子类实现

class ElementA : public Element
{
public:
void accept(Visitor &visitor) override {
visitor.visitElementA(*this);
}
}; class ElementB : public Element
{
public:
void accept(Visitor &visitor) override {
visitor.visitElementB(*this); //第二次多态辨析
} };

=========================上面实现了:预先的设计了我将来可能会增加的新的操作=============================

4.将来,要为前面的整个类层次结构添加新的操作,添加visitor子类,在子类中扩展

//扩展1  将来
class Visitor1 : public Visitor{
public:
void visitElementA(ElementA& element) override{
cout << "Visitor1 is processing ElementA" << endl;
} void visitElementB(ElementB& element) override{
cout << "Visitor1 is processing ElementB" << endl;
}
}; //扩展2  将来的将来
class Visitor2 : public Visitor{
public:
void visitElementA(ElementA& element) override{
cout << "Visitor2 is processing ElementA" << endl;
} void visitElementB(ElementB& element) override{
cout << "Visitor2 is processing ElementB" << endl;
}
};

5.操作绑定

int main()
{
Visitor2 visitor;
ElementB elementB;
elementB.accept(visitor);// double dispatch  将visitor2中的新的visitElementA操作添加到elementB中 ElementA elementA;
elementA.accept(visitor);  //将Visitor2中新的visitElementB新操作添加到了elementA中 return ;
}

四:模式定义

表示一个作用与某对像结构中的各元素的操作。使得可以在不改变(稳定)各元素的类的前提下定义(扩展)作用于这些元素的新操作(变化)。

                                                                                        ——《设计模式》GoF

五:类图(结构)

visitor要预先知道(依赖于)我们具体的ELement子类,所以我们要提前设置好其子类,之后基本不会修改我们修改的只有visitor扩展子类。
所以Element子类是稳定的
(必须提前确定下来,但是常常确定不了,我们一增加element子类,visitor基类就要做出相应改变,子类也是,全部的稳定性都混乱了,打破了开闭原则,全部都要重新编译),
visitor子类是变化的

六:要点总结

(一)Vistor模式通过所谓的双重分发(double dispatch)来实现在不更改(不添加新的操作-编译时)Element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新的操作(支持变化)。

(二)所谓双重分发即Vistor模式中间包括了两个多态分发(注意其中的多态机制):第一个accept方法的多态解析;第二个为visitElementX方法的多态辨析。

(三)Visitor模式最大的缺点在于扩展类层次结构(增添新的Element子类),会导致Visitor类的改变。因此Visitor模式适用于“Element类层次结构稳定,而其中的操作却进场面临频繁改动”。

七:案例实现(公园功能扩展)

比如有一个公园,有一到多个不同的组成部分;多个公园,每个公园存在多个访问者:清洁工负责打扫公园的部分,公园的管理者负责检点各项事务是否完成,上级领导可以视察公园,扩建等等。
也就是说,对于同一个公园,不同的访问者有不同的行为操作,而且访问者的种类也可能需要根据时间的推移而变化(行为的扩展性)。
根据软件设计的开闭原则(对修改关闭,对扩展开放),我们怎么样实现这种需求呢?

(一)实现访问者基类

class ParkElement;

class Visitor    //Visitor是针对每个element的子类,其方法也是针对
{
public:
virtual void visitParkA(ParkElement* park) = ;
virtual void visitParkB(ParkElement* park) = ; virtual ~Visitor(){}
};

(二)实现公园基类

class ParkElement
{
public:
void Clean()
{
cout << "clean part" << endl;
} void Play()
{
cout << "play in part" << endl;
} virtual void accept(Visitor* v) = ;
};

(三)实现子类具体公园

class ParkA:public ParkElement
{
public:
virtual void accept(Visitor* v)
{
v->visitParkA(this);
}
}; class ParkB :public ParkElement
{
public:
virtual void accept(Visitor* v)
{
v->visitParkB(this);
}
};

(四)针对具体公园子类扩展功能

//扩展,这里扩展了两个功能
class Visitor1 :public Visitor
{
public:
virtual void visitParkA(ParkElement* park)
{
cout << "leader check parkA health" << endl;
} virtual void visitParkB(ParkElement* park)
{
cout << "leader check parkB health" << endl;
}
}; class Visitor2 :public Visitor
{
public:
virtual void visitParkA(ParkElement* park)
{
cout << "leader extend parkA area" << endl;
} virtual void visitParkB(ParkElement* park)
{
cout << "leader extend parkB area" << endl;
}
};

(五)进行扩展绑定

void main()
{
Visitor1 visitor;
ParkA pA,pB;
pA.accept(&visitor);
pB.accept(&visitor); system("pause");
return;
}

#include <iostream>
#include <list>
#include <string>
using namespace std; class ParkElement; class Visitor //Visitor是针对每个element的子类,其方法也是针对
{
public:
virtual void visitParkA(ParkElement* park) = ;
virtual void visitParkB(ParkElement* park) = ; virtual ~Visitor(){}
}; class ParkElement
{
public:
void Clean()
{
cout << "clean part" << endl;
} void Play()
{
cout << "play in part" << endl;
} virtual void accept(Visitor* v) = ;
}; class ParkA:public ParkElement
{
public:
virtual void accept(Visitor* v)
{
v->visitParkA(this);
}
}; class ParkB :public ParkElement
{
public:
virtual void accept(Visitor* v)
{
v->visitParkB(this);
}
}; //扩展
class Visitor1 :public Visitor
{
public:
virtual void visitParkA(ParkElement* park)
{
cout << "leader check parkA health" << endl;
} virtual void visitParkB(ParkElement* park)
{
cout << "leader check parkB health" << endl;
}
}; class Visitor2 :public Visitor
{
public:
virtual void visitParkA(ParkElement* park)
{
cout << "leader extend parkA area" << endl;
} virtual void visitParkB(ParkElement* park)
{
cout << "leader extend parkB area" << endl;
}
}; void main()
{
Visitor1 visitor;
ParkA pA,pB;
pA.accept(&visitor);
pB.accept(&visitor); system("pause");
return;
}

全部代码

设计模式---行为变化模式之访问器模式(Visitor)的更多相关文章

  1. Java进阶篇设计模式之五-----外观模式和装饰器模式

    前言 在上一篇中我们学习了结构型模式的适配器模式和桥接模式.本篇则来学习下结构型模式的外观模式和装饰器模式. 外观模式 简介 外观模式隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口.这 ...

  2. Java设计模式之五 ----- 外观模式和装饰器模式

    前言 在上一篇中我们学习了结构型模式的适配器模式和桥接模式.本篇则来学习下结构型模式的外观模式和装饰器模式. 外观模式 简介 外观模式隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口.这 ...

  3. 深入探索Java设计模式(三)之装饰器模式

    装饰器模式使你可以在运行时使用类似于对象组成的技术来装饰类.这在我们希望实例化具有新职责的对象而无需对基础类进行任何代码更改的情况下尤其有用.本文是在学习完优锐课JAVA架构VIP课程—[框架源码专题 ...

  4. IOS设计模式之二(门面模式,装饰器模式)

    本文原文请见:http://www.raywenderlich.com/46988/ios-design-patterns. 由 @krq_tiger(http://weibo.com/xmuzyq) ...

  5. 设计模式---领域规则模式之解析器模式(Interpreter)

    前提:领域规则模式 在特定领域内,某些变化虽然频繁,但可以抽象为某种规则.这时候,结合特定领域,将问题抽象为语法规则,从而给出该领域下的一般性解决方案. 典型模式 解析器模式:Interpreter ...

  6. 设计模式---对象创建模式之构建器模式(Builder)

    一:概念 Builder模式也叫建造者模式或者生成器模式,是由GoF提出的23种设计模式中的一种.Builder模式是一种对象创建型模式之一,用来隐藏复合对象的创建过程,它把复合对象的创建过程加以抽象 ...

  7. 面向对象程序设计(OOP设计模式)-结构型模式之装饰器模式的应用与实现

    课程名称:程序设计方法学 实验4:OOP设计模式-结构型模式的应用与实现 时间:2015年11月18日星期三,第3.4节 地点:理1#208 一.实验目的 加深对结构型设计模式的理解以及在开发中的实际 ...

  8. JS设计模式(三) 数据访问对象模式

    引言 HTML5 提供了两种在客户端存储数据的新方法:localStorage.sessionStorage,他们是Web Storage API 提供的两种存储机制,区别在于前者属于永久性存储,而后 ...

  9. Java设计模式(七)Decorate装饰器模式

    一.场景描述 (一)问题 系统中最初使用Crystal Report(水晶报表)工具生成报表,并将报表发送给客户端查看,此时定义一CrystalReport工具类即可完成水晶报表的生成工作. 后续报表 ...

随机推荐

  1. python 项目启动路径自动添加

    import os import sys base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  #找到当前项目 ...

  2. 洛谷P2512 糖果传递

    环形均分纸牌 普通的均分纸牌前缀和的总和就是答案. 但是这里是环形的,要断开的位置需要最佳,我们把每个数减去sum/n,这样总的前缀和就为0了,若在第k个数之后把环断开,环形前缀和可以统一写成s[i] ...

  3. 【BZOJ3813】【清华集训2014】奇数国 线段树 数学

    题目描述 给你一个长度为\(n\)的数列,第\(i\)个数为\(a_i\).每个数的质因子都只有前\(60\)个质数.有\(q\)个询问,每次给你\(l,r\),求\(\varphi(\prod_{i ...

  4. 《python3网络爬虫开发实战》第一章、开发环境配置-问题汇总

    开发环境: VMware虚拟机 Ubuntu18.04 python3.6 (由于对vi操作水平有限,所以大部分都用的gedit进行文件编辑) 换源: 刚装上系统后换了好几个源,就清华源感觉好使点,别 ...

  5. python学习日记(格式化输出,初始编码,运算符)

    格式化输出 顾名思义,按照个人意愿定制想输出的格式. name = input('请输入姓名:') age = int(input('请输入年龄:')) job = input('请输入工作:') h ...

  6. ElasticSearch5.4.1 搜索引擎搭建文档

    安装配置JDK环境JDK安装(不能安装JRE)JDK下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-download ...

  7. layer 弹出层

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  8. A1126. Eulerian Path

    In graph theory, an Eulerian path is a path in a graph which visits every edge exactly once. Similar ...

  9. [luogu3810][bzoj3262][陌上花开]

    题目链接 思路 听说可以CDQ分治,然后我不会,所以我写树套树 首先肯定先按照a拍个序.然后就成了在b,c这两个数组中查询了.用一个树状数组套treap来维护.当插入一个数的时候,就在树状数组的b这个 ...

  10. (六)Oracle 的 oracle表查询关键字

    参考:http://www.hechaku.com/Oracle/oracle_tables2.html 1.使用逻辑操作符号问题:查询工资高于500或者是岗位为manager的雇员,同时还要满足他们 ...