1 测评系统需求
某餐厅推出了一款饮料,想让品尝着对其评价成功或失败。品尝着可以是男人、女人,评价可以是成功或失败。传统方案是写一个Person类,然后让Man和Woman类继承Person并实现评价功能。传统方案扩展性不好,如果要增加新的人员类型或评价结果,需要修改Person及其子类,违反开闭原则。而访问者模式可以很好地解决这个问题。
2 访问者模式介绍
访问者模式封装了一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作,这种设计模式将数据结构与数据操作分离,解决数据结构和操作耦合性问题。访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口。
角色分析:
1)Visitor:抽象访问者,为该对象结构中的ConcreteElement的每一个类声明一个visit操作。
2)ConcreteVisitor:具体的访问者,实现Visitor声明的操作,是每个操作到的具体实现。
3)ObjectStructure:对象结构,它能枚举具体的元素,可以提供一个高层的接口,来允许访问者访问元素。
4)Element:元素的抽象类,定义一个accept方法,接收一个访问者对象。
5)ConcreteElement:元素的具体实现,实现了accept方法。
3 用访问者模式实现评分需求
首先有抽象的访问者Action:
public abstract class Action {
abstract void getManResult(Person man);
abstract void getWomanResult(Person woman);
}
访问者的具体实现Success、Fail:
public class Success extends Action {
@Override
void getManResult(Person man) {
System.out.println("男人给的评价是:成功");
}
@Override
void getWomanResult(Person woman) {
System.out.println("女人给的评价是:成功");
}
}
public class Fail extends Action {
@Override
void getManResult(Person man) {
System.out.println("男人给的评价是:失败");
}
@Override
void getWomanResult(Person woman) {
System.out.println("女人给的评价是:失败");
}
}
元素的抽象类Person:
public abstract class Person {
abstract void accept(Action action);
}
具体的元素实现类Man、Woman,需要注意的是:这里使用了双分派的方式,首先在客户端程序中,将具体的状态作为参数传递到Man或Woman中(第一次分派),然后在Man或Woman类中调用了作为参数的"具体方法"中的getWomanResult,同时将自己(即this)作为参数传入(第二次分派)。
public class Man extends Person {
@Override
void accept(Action action) {
action.getManResult(this);
}
}
public class Woman extends Person {
@Override
void accept(Action action) {
action.getWomanResult(this);
}
}
ObjectStructure类:
public class ObjectStructure {
// 维护一个集合
private List<Person> persons = new LinkedList<>();
public void attach(Person person) {
persons.add(person);
}
public void detach(Person person) {
persons.remove(person);
}
// 显示测评结果
public void display(Action action) {
for (Person person : persons) {
person.accept(action);
}
}
}
客户端调用:
public class Client {
public static void main(String[] args) {
// 创建ObjectStructure对象
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.attach(new Man());
objectStructure.attach(new Woman());
objectStructure.attach(new Man());
// 成功的评价
Success success = new Success();
objectStructure.display(success);
System.out.println("====================");
// 失败的评价
Fail fail = new Fail();
objectStructure.display(fail);
}
}
输出结果:
男人给的评价是:成功
女人给的评价是:成功
男人给的评价是:成功
====================
男人给的评价是:失败
女人给的评价是:失败
男人给的评价是:失败
4 访问者模式的优缺点
优点:
1)访问者模式符合单一职责原则,可以让程序具有优秀的扩展性和灵活性。
2)访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构稳定地系统。
缺点:
1)对具体元素访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的,这样会造成具体元素的变更比较困难。
2)违背了依赖倒转原则,访问者依赖的是具体元素,而不是抽象类。
因此,如果一个系统具有较稳定地数据结构,又有经常变化的功能需求,那么访问者模式是比较合适的。