设计模式---行为变化模式之访问器模式(Visitor)
一:概念
访问者模式,是行为模式之一,它分离对象的数据和行为,使用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)的更多相关文章
- Java进阶篇设计模式之五-----外观模式和装饰器模式
前言 在上一篇中我们学习了结构型模式的适配器模式和桥接模式.本篇则来学习下结构型模式的外观模式和装饰器模式. 外观模式 简介 外观模式隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口.这 ...
- Java设计模式之五 ----- 外观模式和装饰器模式
前言 在上一篇中我们学习了结构型模式的适配器模式和桥接模式.本篇则来学习下结构型模式的外观模式和装饰器模式. 外观模式 简介 外观模式隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口.这 ...
- 深入探索Java设计模式(三)之装饰器模式
装饰器模式使你可以在运行时使用类似于对象组成的技术来装饰类.这在我们希望实例化具有新职责的对象而无需对基础类进行任何代码更改的情况下尤其有用.本文是在学习完优锐课JAVA架构VIP课程—[框架源码专题 ...
- IOS设计模式之二(门面模式,装饰器模式)
本文原文请见:http://www.raywenderlich.com/46988/ios-design-patterns. 由 @krq_tiger(http://weibo.com/xmuzyq) ...
- 设计模式---领域规则模式之解析器模式(Interpreter)
前提:领域规则模式 在特定领域内,某些变化虽然频繁,但可以抽象为某种规则.这时候,结合特定领域,将问题抽象为语法规则,从而给出该领域下的一般性解决方案. 典型模式 解析器模式:Interpreter ...
- 设计模式---对象创建模式之构建器模式(Builder)
一:概念 Builder模式也叫建造者模式或者生成器模式,是由GoF提出的23种设计模式中的一种.Builder模式是一种对象创建型模式之一,用来隐藏复合对象的创建过程,它把复合对象的创建过程加以抽象 ...
- 面向对象程序设计(OOP设计模式)-结构型模式之装饰器模式的应用与实现
课程名称:程序设计方法学 实验4:OOP设计模式-结构型模式的应用与实现 时间:2015年11月18日星期三,第3.4节 地点:理1#208 一.实验目的 加深对结构型设计模式的理解以及在开发中的实际 ...
- JS设计模式(三) 数据访问对象模式
引言 HTML5 提供了两种在客户端存储数据的新方法:localStorage.sessionStorage,他们是Web Storage API 提供的两种存储机制,区别在于前者属于永久性存储,而后 ...
- Java设计模式(七)Decorate装饰器模式
一.场景描述 (一)问题 系统中最初使用Crystal Report(水晶报表)工具生成报表,并将报表发送给客户端查看,此时定义一CrystalReport工具类即可完成水晶报表的生成工作. 后续报表 ...
随机推荐
- Dividing POJ - 1014 多重背包二进制优化
多重背包模型 写的时候漏了一个等号找了半天 i<<=1 !!!!!! #include<iostream> #include<cstdio> #include&l ...
- Asteroids POJ - 3041 匈牙利算法+最小点覆盖König定理
题意: 给出一个N*N的地图N 地图里面有K个障碍 你每次可以选择一条直线 消除这条直线上的所有障碍 (直线只能和列和行平行) 问最少要消除几次 题解: 如果(x,y)上有一个障碍 则把 ...
- Spring02-注入和注解方式操作
一. 依赖注入 测试类:Person.java 创建配置文件:applicationContext-injection.xml 创建测试代码:InjectionTest.java 1. set方法注入 ...
- spi slaver接口的fpga实现
前言 spi从机接口程序,数据位8bit,sck空闲时低电平,工作时第一个沿数据传输.只有一个从机,cs低电平片选,slaver开始工作. 流程: 接口定义: 编码实现:(版权所有,请勿用于商业用途, ...
- awk文本分析工具
Usage:awk [option] 'program' file ... awk [option] -f progfile file ... [optin]: -F fs 指定输入分隔符,fs可是字 ...
- default.conf
1./etc/nginx/conf.d/ 下设置一个 default.conf,server_name 设置为 localhost,如果有其他非法域名 A 记录到该机器上,则返回默认的 Nginx 页 ...
- multiset和set
set集合容器:实现了红黑树的平衡二叉检索树的数据结构,插入元素时,它会自动调整二叉树的排列,把元素放到适当的位置,以保证每个子树根节点键值大于左子树所有节点的键值,小于右子树所有节点的键值:另外,还 ...
- 对于submit text3运行Python脚本的解决方法
先抛出一些配置信息吧 电脑安装了Python2和Python3两种版本,并且还改了exe文件的名字,可能这与默认的submit text3配置不同了吧,导致错误. 先抛出一些错误吧 首先,自动CTRL ...
- MD5加密解密类(asp.net)&使用MD5过时处理
加密类 #region ========加密======== /// <summary> /// 加密 /// </summary> /// <param name=&q ...
- Django 跨域请求
跨域:通过js或python在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(Django)的数据.只要协议.域名.端口有任何一个不同,都被 ...