《Head First 设计模式》笔记25-访问者模式
定义
访问者模式即表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
首先我们要明确一点就是访问者模式适用于数据结构相对稳定的系统。它是将数据的操作与数据结构进行分离了,如果某个系统的数据结构相对稳定,但是操作算法易于变化的话,就比较适用适用访问者模式,因为访问者模式使得算法操作的增加变得比较简单了。
模式结构
访问者模式主要包含如下几个角色:
- Vistor: 抽象访问者。为该对象结构中的 ConcreteElement 的每一个类声明的一个操作。
- ConcreteVisitor: 具体访问者。实现 Visitor 申明的每一个操作,每一个操作实现算法的一部分。
- Element: 抽象元素。定义一个 Accept 操作,它以一个访问者为参数。
- ConcreteElement: 具体元素 。实现 Accept 操作。
- ObjectStructure: 对象结构。能够枚举它的元素,可以提供一个高层的接口来允许访问者访问它的元素。
在访问者模式中对象结构存储了不同类型的对象,以便不同的访问者来访问。从上面的 UML 结构图中我们可以看出,访问者模式主要分为两个层次结构,一个是访问者层次结构,提供了抽象访问者和具体访问者,主要用于什么一些操作。一个是元素层次结构,提供了抽象元素和具体元素,主要用于声明 Accept 操作。
在访问者模式中相同的访问者可以以不同的方式访问不同的元素,所以在访问者模式中增加新的访问者无需修改现有代码,可扩展行强。
同时在访问者模式用到了一种双分派的技术,所谓双分派技术就是在选择一个方法的时候,不仅仅要根据消息接收者(receiver)的运行时区别(Run time type),还要根据参数的运行时区别。在访问者模式中,客户端将具体状态当做参数传递给具体访问者,这里完成第一次分派,然后具体访问者作为参数的 “具体状态” 中的方法,同时也将自己 this 作为参数传递进去,这里就完成了第二次分派。双分派意味着得到的执行操作决定于请求的种类和接受者的类型。
模式实现
以在医院付费、取药为实例。在这个实例中划价员和药房工作者作为访问者,药品作为访问元素、处方单作为对象结构。
抽象访问者: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);
}
}
模式优缺点
优点
- 使得新增新的访问操作变得更加简单。
- 能够使得用户在不修改现有类的层次结构下,定义该类层次结构的操作。
- 将有关元素对象的访问行为集中到一个访问者对象中,而不是分散搞一个个的元素类中。
- 符合单一职责原则。
缺点
- 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,违背了 “开闭原则” 的要求。
- 破坏封装。当采用访问者模式的时候,就会打破组合类的封装。
- 比较难理解。貌似是最难的设计模式了。
模式适用场景
- 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作 “污染” 这些对象的类,也不希望在增加新操作时修改这些类。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 bin07280@qq.com
文章标题:《Head First 设计模式》笔记25-访问者模式
文章字数:1.5k
本文作者:Bin
发布时间:2018-08-06, 20:15:17
最后更新:2019-08-06, 00:07:35
原始链接:http://coolview.github.io/2018/08/06/Head-First-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E3%80%8AHead-First-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E3%80%8B%E7%AC%94%E8%AE%B025-%E8%AE%BF%E9%97%AE%E8%80%85%E6%A8%A1%E5%BC%8F/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。