访问者模式(Visitor Pattern):算是GoF中最复杂的一个模式了,其目的是把处理从数据结构中分离出来,使用该模式的前提条件是数据结构稳定不变而处理易变。大多时候并不需要访问者模式,因为很难找到数据结构不变的情况
封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作
访问者模式的五个角色:
- 抽象元素(Element)角色:申明一个接受操作,接收一个访问者对象,依赖于抽象访问者角色;
- 抽象访问者(Visitor)角色:声明一个或多个访问操作,定义访问者可以访问哪些元素,即依赖于具体元素;
- 具体元素(Concrete Element)角色:实现抽象元素中的接收操作;
- 具体访问者(Concrete Visitor)角色:实现抽象访问者中的所有访问操作;
- 结构对象(Object Structure)角色:职责如:可以遍历结构中的所有元素,如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素,也可以设计一个复合对象或者一个集合(List/Set/Vector);
适用:
- 数据结构相对稳定的系统;
- 很多系统可以按照算法和数据结构分开,如果数据结构比较稳定不易改变,而算法易于变化的话,使用访问者模式比较适合,其中可变算法就是访问者类;
- 迭代器模式不能胜任的情况下,可以尝试访问者模式(迭代器模式只能访问同类或同接口的数据,而访问者模式就是对迭代器模式的扩充,可以遍历不同的对象,执行不同的操作)
优点:
- 访问者模式使得增加新的操作变得很容易(增加新的操作只需要增加新的访问者类);
- 把数据结构和作用于结构上的操作之间的耦合解开,使得操作可以相对自由地演化;
缺点:
- 使得增加新的数据结构很困难(增加新的数据结构除了需要新增一个具体元素类之外,还需要将该元素类添加进结构对象中以及往抽象访问者中添加一个访问该元素操作的方法,即修改所有访问者类);
- 破坏了封装;
- 违背了依赖倒置原则(访问者依赖的是具体的元素,而不是抽象的元素);
访问者模式基础代码实现:
import java.util.ArrayList;
import java.util.List;
public class VisitorModel {
/*
* 访问者模式——元素角色固定、访问者角色多个
*/
public static void main(String[] args) {
Structure structure=new Structure();//结构角色
//将所有的元素添加进结构中(由于元素个数应该确定、故这一步理应在结构角色类中完成)
structure.addElement(new ConcreteElementA());
structure.addElement(new ConcreteElementB());
//具体访问者通过结构角色访问所有元素中的相应操作
structure.visitElement(new ConcreteVisitor1());//访问者1
structure.visitElement(new ConcreteVisitor2());//访问者2
}
}
//抽象元素角色(Element)——要求具体元素个数稳定不易变
abstract class Element{
public abstract void accept(Visitor visit);//接受访问者访问
}
//抽象访问者角色——要求方法的个数是稳定不易变的
interface Visitor{
public void visitEA(ConcreteElementA a);
public void visitEB(ConcreteElementB b);
}
//具体元素角色
class ConcreteElementA extends Element{
//双分派技术,实现处理与数据结构的分离
@Override
public void accept(Visitor visit) {
visit.visitEA(this);
}
public String getNameA() {
return "ElementA";
}
}
class ConcreteElementB extends Element{
//双分派技术,实现处理与数据结构的分离
@Override
public void accept(Visitor visit) {
visit.visitEB(this);
}
public String getNameB() {
return "ElementB";
}
}
//具体访问者角色
class ConcreteVisitor1 implements Visitor{
@Override
public void visitEA(ConcreteElementA a) {
System.out.println("Visitor1访问了"+a.getNameA());
}
@Override
public void visitEB(ConcreteElementB b) {
System.out.println("Visitor1访问了"+b.getNameB());
}
}
class ConcreteVisitor2 implements Visitor{
@Override
public void visitEA(ConcreteElementA a) {
System.out.println(a.getNameA()+"被Visitor2访问了");
}
@Override
public void visitEB(ConcreteElementB b) {
System.out.println(b.getNameB()+"被Visitor2访问了");
}
}
//其他访问者...
//结构角色——依赖的全是抽象
class Structure{
private List<Element> list;
//初始化集合
public Structure() {
list=new ArrayList<Element>();
}
//添加元素
public void addElement(Element e) {
list.add(e);
}
//移除集合
public void removeElement(Element e) {
list.remove(e);
}
//遍历访问元素
public void visitElement(Visitor visit) {
for(Element e:list) {
e.accept(visit);
}
}
}
运行结果:
Visitor1访问了ElementA
Visitor1访问了ElementB
ElementA被Visitor2访问了
ElementB被Visitor2访问了
参考资料:
《大话设计模式》——程杰
《设计模式(Java版)》