访问者模式
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
数据操作与数据结构分离的设计模式。是 23 种设计模式中比较复杂的一个,使用频率不高但是一旦需要使用,就是迫切的需要。
类结构图
Vistior
是一个接口定义了每一个元素(Element)的行为。方法参数就是可以访问的元素。
ConcreteVisitorA、ConcreteVisitorB
具体访问者,实现每个由 Vistior 声明的操作。
Element
元素接口,它定义了一个接受访问者(accept)的方法,其意义是指,每一个元素都要可以被访问者访问。
ConcreteElementA、ConcreteElementB
具体元素实现了 Element 接口的方法。
ObjectStructure
可以理解为一个具有容器性质或者复合对象特性的类,它会含有一组元素(Element),并且可以迭代这些元素,供访问者访问。
静态绑定、动态绑定
在看代码之前我们需要先简单的复习下什么是静态绑定和动态绑定。
什么是绑定?
一个方法与其所在的类/对象关联起来叫做方法的绑定。
静态绑定
在程序运行前就已经知道方法是属于那个类的,在编译的时候就可以连接到类中,定位到这个方法。在 Java 中,final、private、static 修饰的方法以及构造函数都是静态绑定的,不需程序运行,不需具体的实例对象就可以知道这个方法的具体内容。
动态绑定
在程序运行过程中,根据具体的实例对象才能具体确定是哪个方法。
比如有一个接口或父类叫 A。还有 ConcreteA 类实现或继承了 A a = new ConcreteA();
大家只要记住一句话就可以理解:编译看左,运行看右。
代码示例
public interface Visitor {
void visitConcreteElement(Element element);
}
public class ConcreteVisitorA implements Visitor {
@Override
public void visitConcreteElement(Element element) {
System.out.println(element.getClass().getSimpleName() + " 被 ConcreteVisitorA 访问!");
}
}
public class ConcreteVisitorB implements Visitor {
@Override
public void visitConcreteElement(Element element) {
System.out.println(element.getClass().getSimpleName() + " 被 ConcreteVisitorB 访问!");
}
}
public interface Element {
void accept(Visitor visitor);
}
public class ConcreteElementA implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visitConcreteElement(this);
}
}
public class ConcreteElementB implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visitConcreteElement(this);
}
}
public class ObjectStructure {
private List<Element> elements = new ArrayList<>();
public void addElement(Element element) {
elements.add(element);
}
public void delElement(Element element) {
elements.remove(element);
}
public void accept(Visitor visitor) {
for (Element element : elements) {
element.accept(visitor);
}
}
}
客户端示例
public class Client {
public static void main(String[] args) {
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.addElement(new ConcreteElementA());
objectStructure.addElement(new ConcreteElementB());
Visitor visitor = new ConcreteVisitorA();
objectStructure.accept(visitor);
visitor = new ConcreteVisitorB();
objectStructure.accept(visitor);
}
}
运行结果
ConcreteElementA 被 ConcreteVisitorA 访问!
ConcreteElementB 被 ConcreteVisitorA 访问!
ConcreteElementA 被 ConcreteVisitorB 访问!
ConcreteElementB 被 ConcreteVisitorB 访问!
Disconnected from the target VM, address: '127.0.0.1:51989', transport: 'socket'
Process finished with exit code 0
优点
新增新的操作很容易,因为增加新的操作只需要增加一个 Visitor 的实现就可以了。
缺点
新增新的元素变得困难,违反了开闭原则。所以访问者适用于数据结构相对稳定的系统。