说明
访问者模式(Visitor)表示一个作用于某对象结构中各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
UML
角色:
Visitor 抽象访问者角色:为该对象结构中具体元素角色声明一个访问操作接口。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色,这样访问者就可以通过该元素角色的特定接口直接访问它。
ConcreteVisitor.具体访问者角色:实现Visitor声明的接口。
Element: 定义一个接受访问操作(accept()),它以一个访问者(Visitor)作为参数。
ConcreteElement 具体元素:实现了抽象元素(Element)所定义的接受操作接口。
ObjectStructure 结构对象角色:这是使用访问者模式必备的角色。它具备以下特性:能枚举它的元素;可以提供一个高层接口以允许访问者访问它的元素;如有需要,可以设计成一个复合对象或者一个聚集(如一个列表或无序集合)。
代码
以《大话设计模式》中该模式的基本代码为例,代码如下。
Visitor,为该对象结构中具体元素角色声明一个访问操作接口。
/**
* @author ctl
* @date 2021/1/29
*/
public interface Visitor {
void visitConcreteElementA(ConcreteElementA concreteElementA);
void visitConcreteElementB(ConcreteElementB concreteElementB);
}
ConcreteVisitor1,具体访问者
/**
* @author ctl
* @date 2021/1/29
*/
public class ConcreteVisitor1 implements Visitor {
@Override
public void visitConcreteElementA(ConcreteElementA concreteElementA) {
System.out.println(concreteElementA.getClass().getSimpleName() + "被"
+ this.getClass().getSimpleName() + "访问");
concreteElementA.operationA();
}
@Override
public void visitConcreteElementB(ConcreteElementB concreteElementB) {
System.out.println(concreteElementB.getClass().getSimpleName() + "被"
+ this.getClass().getSimpleName() + "访问");
concreteElementB.operationB();
}
}
ConcreteVisitor2,具体访问者
/**
* @author ctl
* @date 2021/1/29
*/
public class ConcreteVisitor2 implements Visitor {
@Override
public void visitConcreteElementA(ConcreteElementA concreteElementA) {
System.out.println(concreteElementA.getClass().getSimpleName() + "被"
+ this.getClass().getSimpleName() + "访问");
concreteElementA.operationA();
}
@Override
public void visitConcreteElementB(ConcreteElementB concreteElementB) {
System.out.println(concreteElementB.getClass().getSimpleName() + "被"
+ this.getClass().getSimpleName() + "访问");
concreteElementB.operationB();
}
}
Element,定义一个accept操作,它以一个访问者为参数
/**
* @author ctl
* @date 2021/1/29
*/
public interface Element {
void Accept(Visitor visitor);
}
ConcreteElementA,具体元素,实现accept操作
/**
* @author ctl
* @date 2021/1/29
*/
public class ConcreteElementA implements Element {
// 处分利用双分派技术,实现处理于数据结构的分离
@Override
public void Accept(Visitor visitor) {
visitor.visitConcreteElementA(this);
}
// 其他的相关方法
public void operationA() {
System.out.println("com.example.design.behavior.visitor.ConcreteElementA.operationA被调用");
}
}
ConcreteElementB,具体元素,实现accept操作
/**
* @author ctl
* @date 2021/1/29
*/
public class ConcreteElementB implements Element {
@Override
public void Accept(Visitor visitor) {
visitor.visitConcreteElementB(this);
}
public void operationB() {
System.out.println("com.example.design.behavior.visitor.ConcreteElementB.operationB被调用");
}
}
ObjectStructure 结构对象角色,这是使用访问者模式必备的角色,能枚举它的元素,可以提供一个高层接口以允许访问者访问它的元素。
/**
* @author ctl
* @date 2021/1/29
*/
public class ObjectStructure {
private List<Element> elements = new ArrayList<>();
public void attach(Element element) {
elements.add(element);
}
public void detach(Element element) {
elements.remove(element);
}
public void accept(Visitor visitor) {
for (Element element : elements) {
element.Accept(visitor);
}
}
}
测试类
/**
* @author ctl
* @date 2021/1/29
*/
public class VisitorMain {
public static void main(String[] args) {
ObjectStructure o = new ObjectStructure();
o.attach(new ConcreteElementA());
o.attach(new ConcreteElementB());
ConcreteVisitor1 v1 = new ConcreteVisitor1();
ConcreteVisitor2 v2 = new ConcreteVisitor2();
o.accept(v1);
o.accept(v2);
}
}
结果
访问者模式可以说是最复杂和难以理解的一个设计模式,所以本次直接用了跟uml对应的代码来做示例。
总结
访问者模式适用于数据结构相对稳定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。
访问者模式的优点是增加新的操作较容易,也就是增加一个新的访问者;而缺点就是增加新的数据结构变得困难了。
“访问者模式的能力和复杂性是一把双刃剑,只有当你真正需要它的时候,才考虑使用它。有的程序员为了展示自己面向对象的能力或沉迷于模式当中,往往会误用这个模式,所以一定要好好理解它的适用性。” ————《大话设计模式》