设计模式---行为变化模式之访问器模式(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工具类即可完成水晶报表的生成工作. 后续报表 ...
随机推荐
- js弹框的3种方法
js的三种弹框的方法 1.第一种 : alert("1"); 2.第二种 : window.open("Tests2.html"); var r = con ...
- Tournament ZOJ - 4063 (青岛区域赛 F 打表)
打表题.. 规律是找出来了 奈何优化不了 .... #include <iostream> #include <cstdio> #include <sstream> ...
- 【 HDU3294 】Girls' research (Manacher)
BUPT2017 wintertraining(15) #5F HDU - 3294 题意 给定字母x,字符串变换一下: 'x'-1 -> 'z', 'x'->'a', 'x'+1-> ...
- 数论细节梳理&模板
初阶 扩展欧拉 \(k\ge\varphi(m)\)时,\(b^k\equiv b^{k\%\varphi(m)+\varphi(m)}(\bmod m\)) 扩展CRT 推式子合并同余方程. htt ...
- 【洛谷2252&HDU1527】取石子游戏(博弈论)
题面 HDU1527 取石子游戏 洛谷2252 取石子游戏 题解 裸的威佐夫博弈 #include<iostream> #include<cmath> using namesp ...
- ⌈洛谷1505⌋⌈BZOJ2157⌋⌈国家集训队⌋旅游【树链剖分】
题目链接 [洛谷] [BZOJ] 题目描述 Ray 乐忠于旅游,这次他来到了T 城.T 城是一个水上城市,一共有 N 个景点,有些景点之间会用一座桥连接.为了方便游客到达每个景点但又为了节约成本,T ...
- 前端开发环境webstorm搭建
1. 下载node.js https://nodejs.org/en/ 2. 安装 webpack 用管理员开个命令行 (mac: open terminal) npm install webpack ...
- dns配置文件
/etc/resolv.conf 该文件是DNS域名解析的配置文件,它的格式很简单,每行以一个关键字开头,后接配置参数. resolv.conf的关键字主要有四个,分别是: nameserver ...
- [POI2005]DWU-Double-row
有2n个士兵站成两排,他们需要被重新排列,以保证每一排里没有同样高的士兵——这样我们就说,士兵们被合理地安排了位置. 每次操作可以交换两个在同一位置(但不在同一排)的士兵.你的任务是用最少的操作来确保 ...
- thinkphp5 上传服务器后 Access denied
服务器报 Access denied,要么报 No input files,但是在网上查了查说是将 PHP 的cgi.fix_pathinfo 改成 1 即可,但是改成 1 显然是有解析漏洞的,尝试寻 ...