设计模式学习笔记十二:访问者模式
介绍
访问者(visitor)模式,属于对象行为型模式,它实现了结构与行为的解耦。
意图:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。(本段摘自《GoF设计模式》 5.11)
适用场景
访问者模式适合对象的结构稳定,行为变动频繁的情况。反之,结构变动频繁的场景不适合使用次模式。在后面的实现中可以很容易的证实这一点,结构的变化会增加更多繁复的操作。在一个已经的接口中增加方法会导致在其所有的实现类中都进行一次实现实现,没人愿意这么做。
UML结构图:
可以先略过,回头再看。
场景模拟
这里我以客户回访场景尝试解释访问者模式的应用:在公司Z中,会定期对他的客户进行回访。根据客户重要度的不同,人物、时间、地点也会不同。不管是否愿意承认,生活中到处充满着等级的划分不是么?
+ 普通客户:铜牌业务员A;时间:工作日;地点:电话连线。
+ 潜力优质客户:银牌业务员B;时间:沟通确定;地点:茶楼。
+ 优质客户:金牌业务员C;时间:沟通确定;地点:避暑山庄。
代码实现:
1.回访操作
package com.array7.visitor;
/**
* 回访接口,即Element
*/
public interface IRevisit {
void accpet(IWorkerVisitor visitor);
}
/**
* 约定的业务员,即ConcreteElement
*/
public class Worker implements IRevisit {
@Override
public void accpet(IWorkerVisitor visitor) {
visitor.visit(this);
}
}
/**
* 约定的回访时间,即ConcreteElement
*/
public class Time implements IRevisit {
@Override
public void accpet(IWorkerVisitor visitor) {
visitor.visit(this);
}
}
/**
* 约定的地点,即ConcreteElement
*/
public class Place implements IRevisit {
@Override
public void accpet(IWorkerVisitor visitor) {
visitor.visit(this);
}
}
/**
* 最终回访单,即ConcreteElement
*/
public class Revisit implements IRevisit {
private IRevisit[] revisits;
public Revisit(IRevisit... revisits) {
this.revisits = revisits;
}
@Override
public void accpet(IWorkerVisitor visitor) {
for (IRevisit revisit : revisits) {
revisit.accpet(visitor);
}
visitor.visit(this);
}
}
2.访问者操作
package com.array7.visitor;
/**
* 访问者接口,即Visitor
*/
public interface IWorkerVisitor {
void visit(Worker worker);
void visit(Time time);
void visit(Place place);
void visit(Revisit revisit);
}
/**
* 金牌业务员访问者,即concreteVisitor
*/
public class CustomerAVisitor implements IWorkerVisitor {
@Override
public void visit(Worker worker) {
System.out.println("安排金牌业务员A回访客户");
}
@Override
public void visit(Time time) {
System.out.println("安排金牌业务员A与客户约定时间:2015-07-26");
}
@Override
public void visit(Place place) {
System.out.println("安排金牌业务员A与客户约定地点:避暑山庄");
}
@Override
public void visit(Revisit revisit) {
System.out.println("金牌业务员A开始回访");;
}
}
/**
* 银牌业务员访问者,即concreteVisitor
*/
public class CustomerBVisitor implements IWorkerVisitor {
@Override
public void visit(Worker worker) {
System.out.println("安排银牌业务员B回访客户");
}
@Override
public void visit(Time time) {
System.out.println("安排银牌业务员B与客户约定时间:2015-07-26");
}
@Override
public void visit(Place place) {
System.out.println("安排银牌业务员B与客户约定地点:茶楼");
}
@Override
public void visit(Revisit revisit) {
System.out.println("银牌业务员B开始回访");
}
}
/**
* 铜牌业务员访问者,即concreteVisitor
*/
public class CustomerCVisitor implements IWorkerVisitor {
@Override
public void visit(Worker worker) {
System.out.println("安排铜牌业务员C回访客户");
}
@Override
public void visit(Time time) {
System.out.println("铜牌业务员C不需要与客户约定时间");
}
@Override
public void visit(Place place) {
System.out.println("铜牌业务员C不需要与客户约定地点");
}
@Override
public void visit(Revisit revisit) {
System.out.println("铜牌业务员C开始回访");
}
}
3.Run
public class Run {
public static void main(String[] args) {
IRevisit revisitA = new Revisit(new Worker(), new Time(), new Place());
revisitA.accpet(new CustomerAVisitor());
System.out.println();
IRevisit revisitB = new Revisit(new Worker(), new Time(), new Place());
revisitB.accpet(new CustomerBVisitor());
System.out.println();
IRevisit revisitC = new Revisit(new Worker(), new Time(), new Place());
revisitC.accpet(new CustomerCVisitor());
}
}
输出
安排金牌业务员A回访客户
安排金牌业务员A与客户约定时间:2015-07-26
安排金牌业务员A与客户约定地点:避暑山庄
金牌业务员A开始回访
安排银牌业务员B回访客户
安排银牌业务员B与客户约定时间:2015-07-26
安排银牌业务员B与客户约定地点:茶楼
银牌业务员B开始回访
安排铜牌业务员C回访客户
铜牌业务员C不需要与客户约定时间
铜牌业务员C不需要与客户约定地点
铜牌业务员C开始回访
策略模式与访问者模式的区别
之前有讲过策略设计模式(http://blog.csdn.net/array7/article/details/43833921),仔细考虑发现本文中的场景适用策略模式也同样可以实现,并且会比适用访问者模式更加简单。下面来对两者进行一下比较:
+ 两者都属于对象行为型模式,同时都体现了对算法的封装,并且都可以在运行时动态指定算法。
+ 区别体现在策略模式只有一个上下文,策略模式的结构决定了Context只能传递一个策略进入。访问者模式则可以是在实现化Element结构之后,随时可以指定多个访问者。虽然在场景模拟
中没有特意做区分,但是通过代码的实现的我们发现是可以很容易做到的。
+ 如果策略模式的算法部分不放在构造对象中传入,而使用accpet方法传入,那么这个策略模式就是一个访问者模式的实现了。So,策略模式也可以看成是访问者模式的一个特例。就像工厂方法模式是抽象工厂模式的一个特例一样。
总结
从上面的实现来看引证了适用场景
中说的访问者模式适合对象的结构稳定,行为变动频繁的情况。反之,结构变动频繁的场景不适合使用次模式
。
+ 在结构不变的情况,增加行为即增加访问者是很容易的,只要新政一个访问者的实现即可。
+ 如果更改了结构(如Element的增删改),访问者接口要发生变动,同时所有访问者实现都要对应调整。
以上是我理解的访问者模式,了解优劣才知何时适用。
不准确之处还请指正,十分感谢!