您的位置:首页 > 移动开发 > Android开发

《Android源码设计模式解析与实战》读书笔记(十六)——访问者模式

2017-05-24 11:47 127 查看
访问者模式是一种将数据操作和数据结构分离的设计模式,它是23种设计模式中最复杂的一个,但是它的使用频率并不高。刚开始我看到这句话的时候又头疼了,而且看它的概念确实很难懂,于是一边看书一边参考一下网上的资料,也算是通过例子简单的理解了它的使用场景和基本实现。

参考资料:http://blog.csdn.net/chenssy/article/details/12029633

大多数情况下并不需要使用访问者模式,但是一旦需要使用它时,那就是真的需要了。有时候我们对同一个对象可能会有不同的处理,对相同元素对象也可能存在不同的操作方式,这时候访问者模式就能很好的解决问题了。

第十六章 访问者模式

1.定义

封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。

2.使用场景

1).对象结构比较稳定,但经常需要在此对象结构上定义新的操作。

2).需要对一个对象结构中的对象进行很多不同且不相关的操作,但需要避免这些操作影响这些对象的类,也要避免增加新操作时修改这些类。

3.简单实现

先写一下访问者模式的各个角色介绍。

Visitor :抽象访问者,一般为接口或者抽象类,它定义了对每一个元素访问的行为,它的参数就是可以访问的元素,一般来说,它的方法个数和元素个数相同,所以访问者模式的元素的类族一般要稳定,如果经常添加、移除元素类,则会导致频繁修改 Visitor 接口,不适合使用访问者模式。

ConcreteVisitor :具体访问者,实现接口或继承抽象类,在方法中实现对每一个元素类访问时的具体行为。

Element :抽象元素,一般为接口或抽象类,它定义了一个接收访问者( accept )的方法,其意义是指每一个元素都可以被访问者访问。

ElementA 、 ElementB ...:具体的元素类,提供接收访问者方法的具体实现。

ObjectStructure :对象结构,它是一个抽象表述,内部管理了元素集合,并且提供方法使访问者可以迭代访问这些元素。

假设这样一个场景,去医院看病拿药,医生会给你开一张处方单,然后你会拿着处方单去划价处划价付钱,再然后才会拿着处方单去配药处拿药。在这个过程中,划价处首先对处方单进行了操作,根据处方单上的药名计算价格,他关心的是药名以及价格,然后配药处也对处方单进行了操作,根据药名配药,他只关心药名而不管价格。将每个药品看作一个对象元素,处方单就是管理这些元素的对象结构,划价处和配药处分别对同一个对象进行了不同的操作,他们俩就是具体访问者,然后用代码来实现这个场景。

抽象访问者:

public abstract class Visitor {
String name;

public Visitor(String name) {
this.name = name;
}

public abstract void visitBanlangen(Banlangen banlangen);

public abstract void visitGankang(Gankang gankang);
}


具体访问者(划价处和配药处):

public class PricingVisitor extends Visitor {
public PricingVisitor(String name) {
super(name);
}

@Override
public void visitBanlangen(Banlangen banlangen) {
System.out.println(name + "-药名" + banlangen.getName() + ",价格" + banlangen.getPrice());
}

@Override
public void visitGankang(Gankang gankang) {
System.out.println(name + "-药名" + gankang.getName() + ",价格" + gankang.getPrice());
}
}


public class DispensaryVisitor extends Visitor {
public DispensaryVisitor(String name) {
super(name);
}

@Override
public void visitBanlangen(Banlangen banlangen) {
System.out.println(name + "-药名:" + banlangen.getName());
}

@Override
public void visitGankang(Gankang gankang) {
System.out.println(name + "-药名:" + gankang.getName());
}
}


抽象元素类,定义了一个三个属性(药名、价格和一天几次),而且有一个 accept() 方法,使其可以被访问者访问:

public abstract class Medicine {
String name;
double price;
int times;

public Medicine(String name, double price, int times) {
this.name = name;
this.price = price;
this.times = times;
}

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 int getTimes() {
return times;
}

public void setTimes(int times) {
this.times = times;
}

public abstract void accept(Visitor visitor);
}


具体元素类(感康和板蓝根):.

public class Gankang extends Medicine {

public Gankang(String name, double price, int times) {
super(name, price, times);
}

@Override
public void accept(Visitor visitor) {
visitor.visitGankang(this);
}
}


public class Banlangen extends Medicine {

public Banlangen(String name, double price, int times) {
super(name, price, times);
}

@Override
public void accept(Visitor visitor) {
visitor.visitBanlangen(this);
}
}


对象结构(处方单):

public class Prescription {
private List<Medicine> medicineList = new ArrayList<>();

public void accept(Visitor visitor) {
for (Medicine aMedicineList : medicineList) {
aMedicineList.accept(visitor);
}
}

public void addMedicine(Medicine medicine) {
if (!medicineList.contains(medicine)) {
medicineList.add(medicine);
}
}

public void removeMedicine(Medicine medicine) {
if (!medicineList.contains(medicine)) {
medicineList.remove(medicine);
}
}
}


在客户端调用:

Prescription mPrescription = new Prescription();
mPrescription.addMedicine(new Gankang("感康", 13.5, 2));
mPrescription.addMedicine(new Banlangen("板蓝根", 7.9, 3));

PricingVisitor mPricingVisitor = new PricingVisitor("划价处");
mPrescription.accept(mPricingVisitor);

DispensaryVisitor mDispensaryVisitor = new DispensaryVisitor("药房");
mPrescription.accept(mDispensaryVisitor);


运行程序后打印如下:



可见不同的访问者对同一个对象执行了不同操作。

对于访问者模式来说,要增加新的访问操作非常容易,比如需要住院时护士也会对这张处方单进行操作,要叮嘱你按规定一天服用几次,但是她不用关心价格,那么我们只需要增加一个护士类访问者:

public class NurseVisitor extends Visitor {
public NurseVisitor(String name) {
super(name);
}

@Override
public void visitBanlangen(Banlangen banlangen) {
System.out.println(name + "-药名" + banlangen.getName() + ",服用几次:" + banlangen.getTimes());
}

@Override
public void visitGankang(Gankang gankang) {
System.out.println(name + "-药名" + gankang.getName() + ",服用几次:" + gankang.getTimes());
}
}


调用的时候替换一下访问者即可:

NurseVisitor mNurseVisitor = new NurseVisitor("护士");
mPrescription.accept(mNurseVisitor);

这样就达到了数据操作和数据对象分离的效果,但是访问者模式要增加一个新的元素类就比较麻烦了,如果新增了一种药品“藿香正气液”(Huoxiangzhengqiye),那么抽象访问者得增加一个 visitHuoxiangzhengqiye() 方法,每一个具体访问者也得去新增一个 visitHuoxiangzhengqiye() 方法,并且加入具体实现,这样修改成本就太大了,所以使用访问者模式一般都要求对象结构稳定。

4.总结

在使用一个模式时要明确它的使用场景、能解决什么问题,访问者模式一般不常用,概念也比较复杂,但是如果需要的时候,那就真的是需要了。

优点:

1).各角色职责分离,符合单一职责原则。

2).优秀的扩展性。

3).使数据结构与数据操作解耦,让操作集合可以独立变化。

缺点:

1).具体元素对访问者公开细节,违反迪米特原则。

2).具体元素变更时修改成本大,违反开闭原则。

3).依赖具体类而不是抽象类,违反依赖倒置原则。

Demo下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: