一:概念

访问者模式,是行为模式之一,它分离对象的数据和行为,使用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. BZOJ2738矩阵乘法——整体二分+二维树状数组

    题目描述 给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数. 输入   第一行两个数N,Q,表示矩阵大小和询问组数:接下来N行N列一共N*N个数,表示这个矩阵:再接下来Q行每行5 ...

  2. TP5.x——打印SQL语句

    操作 使用fetchSql,然后sql就会只输出sql语句而不执行 var_dump(Db::name('user')->where(array('id'=>$this->_uid, ...

  3. Windows server 2012 卸载iis 黑屏

    转载:https://jingyan.baidu.com/article/ff42efa9fb6f16c19e2202dc.html 1 按  ctrl+alt+end 键,新建运行任务 ,输入cmd ...

  4. Django ORM模型

    Object Relational Mapping(ORM) 一,ORM介绍 1, ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象 ...

  5. Marriage Match IV HDU - 3416(最短路 + 最大流)

    题意: 求有多少条最短路 解析: 正着求一遍最短路 得dis1 反着求一遍得 dis2   然后 遍历所有的边 如果  dis1[u] + dis2[v] + w == dis1[B], 则说明这是一 ...

  6. 【刷题】BZOJ 1413 [ZJOI2009]取石子游戏

    Description 在研究过Nim游戏及各种变种之后,Orez又发现了一种全新的取石子游戏,这个游戏是这样的: 有n堆石子,将这n堆石子摆成一排.游戏由两个人进行,两人轮流操作,每次操作者都可以从 ...

  7. 自学华为IoT物联网之路

    自学华为IoT物联网之路 01 自学华为IoT物联网_01 物联网概述 02 自学华为IoT物联网_02 常见物联网通信技术 03 自学华为IoT物联网_03 公共事业物联网常见问题及解决方案 04  ...

  8. 【CF526G】Spiders Evil Plan(贪心)

    [CF526G]Spiders Evil Plan(贪心) 题面 洛谷 CodeForces 给定一棵树,要求选择\(y\)条链,满足被链覆盖的所有点在树上联通,且\(x\)必定在联通块中. 对于每次 ...

  9. QBXT Day2主要是数据结构(没写完先占坑)

    简单数据结构 本节课可能用到的一些复杂度: O(log n). 1/1+1/1/.....1/N+O(n log n) 在我们初学OI的时候,总会遇到这么一道题. 给出N次操作,每次加入一个数,或者询 ...

  10. 洛谷P2824 排序

    解:splay + 线段树合并,分裂. 首先有个乱搞做法是外层拿splay维护,有序区间缩成splay上一个节点.内层再开个数据结构支持合并分裂有序集合. 内层我一开始想的是splay,然后就没有复杂 ...