背景:

去医院看病时,医生会给你一个处方单要你去拿药,拿药我们可以分为两步走:

  • (1)去柜台交钱,划价人员会根据处方单上的药进行划价,交钱。
  • (2)去药房拿药,药房工作者同样根据处方单给你相对应的药。

这里我们就划价和拿药两个步骤进行讨论,这里有三个类,处方单(药)、划价人员、药房工作者。同时划价人员和药房工作者都各自有一个动作:划价、拿药。这里进行最初步的设计如下:

划价人员:

public class Charge {public void action(){public void action(){if("A药".equals(medicine)){//A的价格}if("B药".equals(medicine)){//B的价格}if("C药".equals(medicine)){//C的价格}if("D药".equals(medicine)){//D的价格}if("E药".equals(medicine)){//E的价格}............}}
}

药房工作者:

public class WorkerOfPharmacy {public void action(){if("A药".equals(medicine)){//给你A药}if("B药".equals(medicine)){//给你B药}if("C药".equals(medicine)){//给你C药}if("D药".equals(medicine)){//给你D药}if("E药".equals(medicine)){//给你E药}............}
}

这样的代码写法,在药品种类少的情况没什么问题,但也存在这么多的 if…else,而且我们可以想象医院里的药是那么多,而且随时都会增加的,增加了药就要改变划价人员和药房工作者的代码,这是我们最不希望改变的。那么有没有办法来解决呢?有,访问者模式提供一中比较好的解决方案。

在实际开发过程中,我们对同个对象可能存在不同的操作方式,如处方单,划价人员要根据它来划价,药房工作者要根据它来给药。而且可能会随时增加新的操作,如医院增加新的药物,但是这里有两个元素是保持不变的,或者说很少变:划价人员和药房工作中,变的只不过是他们的操作。所以我们想如果能够将他们的操作抽象化就好了,这里访问者模式就是一个值得考虑的解决方案了。

一、什么是访问者模式:

访问者模式适用于数据结构相对稳定的系统,将数据结构与基于数据的操作进行分离,使得添加作用于这些数据结构的新操作变得简单,并且不需要改变各数据结构,为不同类型的数据结构提供多种访问操作方式,这样是访问者模式的设计动机。

除了使新增访问操作变得更加简单,也能够在不修改现有类的层次结构下,定义该类层次结构的操作,并将有关元素对象的访问行为集中到一个访问者对象中,而不是分散搞一个个的元素类中。

但访问者模式的缺点在于让增加新的元素类变得困难,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,违背了“开闭原则”的要求;

所以访问者模式适用于对象结构中很少改变,但经常需要在此对象结构上定义新的操作的系统,使得算法操作的增加变得简单;或者需要对一个对象结构中进行很多不同并且不相关的操作,并且需要避免让这些操作污染这些对象,也不希望在增加新操作时修改这些类的场景

二、UML结构图:

  • Vistor:抽象访问者,声明了对 ConcreteElement 类的一些操作
  • ConcreteVisitor:具体访问者,实现抽象访问者中声明的每一个操作
  • Element:抽象元素,定义一个 accept 操作,用于接收具体访问者
  • ConcreteElement:具体元素 ,实现 accept 操作。
  • ObjectStructure:对象结构,提供一个高层接口来允许访问者枚举它的元素

从上面的 UML结构图中我们可以看出,访问者模式主要分为两个层次结构,一个是访问者层次结构,提供了抽象访问者和具体访问者,主要用于声明一些操作;一个是元素层次结构,提供了抽象元素和具体元素,主要用于声明 accept 操作;而对象结构作为两者的桥梁,存储了不同类型的对象,以便不同的访问者来访问,相同访问者可以以不同的方式访问不同的元素,所以在访问者模式中增加新的访问者无需修改现有代码,可扩展行强。

在访问者模式使用了双分派技术,所谓双分派技术就是在选择方法的时候,不仅仅要根据消息接收者的运行时区别,还要根据参数的运行时区别。在访问者模式中,客户端将具体状态当做参数传递给具体访问者,这里完成第一次分派,然后具体访问者作为参数的“具体状态”中的方法,同时也将自己this作为参数传递进去,这里就完成了第二次分派。双分派意味着得到的执行操作决定于请求的种类和接受者的类型。

二、模式的实现:

以上面在医院付费、取药为实例。在这个实例中划价员和药房工作者作为访问者,药品作为访问元素、处方单作为对象结构,所以整个UML结构图如下:

抽象访问者:Visitor.java

public abstract class Visitor {protected String name;public void setName(String name) {this.name = name;}public abstract void visitor(MedicineA a);public abstract void visitor(MedicineB b);
}

具体访问者:划价员、Charger.java

public class Charger extends Visitor{public void visitor(MedicineA a) {System.out.println("划价员:" + name +"给药" + a.getName() +"划价:" + a.getPrice());}public void visitor(MedicineB b) {System.out.println("划价员:" + name +"给药" + b.getName() +"划价:" + b.getPrice());}
}

具体访问者:药房工作者、WorkerOfPharmacy.java

public class WorkerOfPharmacy extends Visitor{public void visitor(MedicineA a) {System.out.println("药房工作者:" + name + "拿药 :" + a.getName());}public void visitor(MedicineB b) {System.out.println("药房工作者:" + name + "拿药 :" + b.getName());}
}

抽象元素:Medicine.java

public abstract class Medicine {protected String name;protected double price;public Medicine (String name,double price){this.name = name;this.price = price;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}public abstract void accept(Visitor visitor);
}

具体元素:MedicineA.java

public class MedicineA extends Medicine{public MedicineA(String name, double price) {super(name, price);}public void accept(Visitor visitor) {visitor.visitor(this);}
}

具体元素:MedicineB.java

public class MedicineB extends Medicine{public MedicineB(String name, double price) {super(name, price);}public void accept(Visitor visitor) {visitor.visitor(this);}
}

药单:Presciption.java

public class Presciption {List<Medicine> list = new ArrayList<Medicine>();public void accept(Visitor visitor){Iterator<Medicine> iterator = list.iterator();while (iterator.hasNext()) {iterator.next().accept(visitor);}}public void addMedicine(Medicine medicine){list.add(medicine);}public void removeMedicien(Medicine medicine){list.remove(medicine);}
}

客户端:Client.java

public class Client {public static void main(String[] args) {Medicine a = new MedicineA("板蓝根", 11.0);Medicine b = new MedicineB("感康", 14.3);Presciption presciption = new Presciption();presciption.addMedicine(a);presciption.addMedicine(b);Visitor charger = new Charger();charger.setName("张三");Visitor workerOfPharmacy = new WorkerOfPharmacy();workerOfPharmacy.setName("李四");presciption.accept(charger);System.out.println("-------------------------------------");presciption.accept(workerOfPharmacy);}
}

运行结果:


设计模式系列文章:

Java设计模式之创建型:工厂模式详解(简单工厂+工厂方法+抽象工厂)

Java设计模式之创建型:建造者模式

Java设计模式之创建型:单例模式

Java设计模式之创建型:原型模式

Java设计模式之结构型:适配器模式

Java设计模式之结构型:装饰器模式

Java设计模式之结构型:代理模式

Java设计模式之结构型:桥接模式

Java设计模式之结构型:外观模式

Java设计模式之结构型:组合模式

Java设计模式之结构型:享元模式

Java设计模式之行为型:策略模式

Java设计模式之行为型:模板方法模式

Java设计模式之行为型:责任链模式

Java设计模式之行为型:观察者模式

Java设计模式之行为型:访问者模式

Java设计模式之行为型:中介者模式

Java设计模式之行为型:命令模式

Java设计模式之行为型:状态模式

Java设计模式之行为型:备忘录模式

Java设计模式之行为型:迭代器模式

Java设计模式之行为型:解释器模式

Java设计模式之行为型:访问者模式相关推荐

  1. 【Java设计模式】简单学访问者模式——我的选择是,Yes

    目录 说明 实现方式 优点 缺点 应用场景 其他链接 说明 行为型模式之一,其他还有命令模式.模板方法模式.迭代器模式.观察者模式.中介者模式.备忘录模式.解释器模式(Interpreter模式).状 ...

  2. JAVA设计模式(14) —行为型模板方法模式(Template Method)

    1 定义: 模板方法模式(Template Method) Define the skeleton of an algorithm in anoperation, deferring some ste ...

  3. Java设计模式之创建型-建造者模式 (Builder)

  4. Java设计模式之行为型:解释器模式

    一.什么是解释器模式:         解释器模式,就是定义语言的文法,并建立一个解释器来解释该语言中的句子,通过构建解释器,解决某一频繁发生的特定类型问题实例. 这里我们将语言理解成使用规定格式和语 ...

  5. Java设计模式之行为型:状态模式

    背景: 介绍状态模式前,我们先看这样一个实例:公司力排万难终于获得某个酒店的系统开发项目,并且最终落到了你的头上.下图是他们系统的主要工作: 当第一眼看到这个系统时你就看出这是一个状态图,每个框都代表 ...

  6. Java设计模式之行为型:备忘录模式

    在开发过程中,经常需要保存对象的中间状态,当需要的时候,可以恢复到这个状态.比如,在编程时假如编写失误,例如不小心误删除了几行代码,我们希望返回删除前的状态,便可以使用 Ctrl+Z 来进行返回,这时 ...

  7. Java设计模式之行为型:命令模式

    前言: 在开发中,我们可能需要向某些对象发送一些请求,但我们不知道请求的具体接收者是谁,也不知道被请求的操作是哪个,只知道在系统运行中指定具体的请求接收者即可,打个比方,电视遥控器,我们只需知道按哪个 ...

  8. Java设计模式之行为型:责任链模式

    背景: 学校规定参加校招的同学必须要请假,且要有相关人员的签字,三天以下需辅导员签字.三到七天需要系主任签字,一个星期以上需要院长签字,更多的则需要校长签字! 上图将学生.辅导员.系主任.院长.校长组 ...

  9. Java设计模式之行为型:迭代器模式

    一.什么是迭代器模式: 实际开发中,我们针对不同的需求,可能需要以不同的方式来遍历整个整合对象,但我们不希望在集合容器的抽象接口层中充斥着各种不同的遍历操作,这时候我们就需要一种能完成下面功能的迭代器 ...

最新文章

  1. 综述 | Google-斯坦福发布深度学习统计力学
  2. 外观设计模式(门面模式)
  3. 安装64位Oracle 10g超详细教程
  4. python print进度条效果
  5. 如何进入python程序代码编辑环境_Python怎么打开代码编辑器 来学习吧
  6. JVM详解之:汇编角度理解本地变量的生命周期
  7. Canvas实战---模仿GOOGLE浮动小球效果
  8. IT程序员必知!TCP/IP为什么会有这么多的致命漏洞?
  9. 2015 - Deep recurrent q-learning for partially observable MDPs
  10. [SVN] 分支同步、合入主干操作分享
  11. USACO 2016 January Contest, Gold解题报告
  12. cant find python executable_gyp ERR! stack Error: Can't find Python executable 'python'
  13. 酒吧手机游戏java_酒吧里24种常见游戏的玩法
  14. 计算机维护工作周报,运维周报怎么写呀,这一周没什么事做
  15. 笔记: 《分布式系统:概念与设计》第一章 分布式系统的特征
  16. windows系统服务器怎么锁屏,使用Windows 8的十个小贴士:自定义锁屏
  17. HTML播放器快进不显示进度条,MediaSource播放视频,快进(直接点击进度条)的时候怎么知道range范围呢...
  18. 回归评价指标:MAE、MSE、RMSE、MAPE和R-Squared
  19. 从Antd 源码到自我实现之 Form表单
  20. 第三周java实验报告

热门文章

  1. 五十二、Java连接Mysql数据库
  2. python的shell无法输入_python中shell如何逐行输入?
  3. python中的单下划线和双下划线_python中的单下划线和双下划线
  4. 推荐系统入门必读:一文读懂推荐系统负采样
  5. 深入解读首个万亿级语言模型Switch Transformer
  6. 快手-中科大最新研究:利用对话式推荐解决用户冷启动问题
  7. 从动力学角度看优化算法:为什么学习率不宜过小?
  8. 本周 Github 精选:13 款炼丹利器,有开源工具包也有超大数据集
  9. 浅谈 翻硬币游戏【Nim博弈】
  10. poj3254 Corn Fields 状压DP入门