设想你是一个艺术馆的管理员,艺术馆里有各种各样的艺术品。每当有游客来访时,根据他们的兴趣,他们可能只想看画、雕塑或特定的展览。在这里,每位游客都有不同的“访问”行为,而艺术馆提供了他们所能“访问”的物品。在软件开发中,我们经常遇到需要对一个复杂的对象结构(如一个元素集合)执行不同操作的情况,而不希望修改对象的类。访问者模式提供了一种将操作逻辑与对象结构分离的方法,使得我们可以在不修改已有程序结构的情况下,向程序添加新的操作。
访问者模式(Visitor Pattern)是一种行为型设计模式,它允许你在不改变各元素类的前提下,定义作用于这些元素的新操作。它使用一种访问者类,改变元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者变化而变化。
实现访问者模式通常涉及以下几个关键组件:
访问者模式适用于以下场景:
例如:
在Spring框架中,BeanDefinitionVisitor 是访问者模式的一个实际应用例子,它用于访问和修改 BeanDefinition 对象。BeanDefinition 对象包含了Spring容器中bean的配置信息,如类名、属性值和构造函数参数。
作用与用途:
BeanDefinitionVisitor 主要用于在Spring容器启动过程中修改已经加载的 BeanDefinition。它通过访问者模式允许开发者编写自定义逻辑来检查和修改 BeanDefinition,而不需要修改 BeanDefinition 的源代码。
如何工作:
访问Bean定义: BeanDefinitionVisitor 遍历Bean定义中的属性值和构造函数参数等信息。
修改Bean定义: 它可以根据需要修改这些信息,例如解析占位符、应用属性编辑器或者更改属性值。
扩展性和灵活性: 开发者可以扩展 BeanDefinitionVisitor 类并覆盖相应的方法来实现自定义访问和修改逻辑。
使用场景:
属性占位符替换: PropertyPlaceholderConfigurer 是Spring中一个常见的使用 BeanDefinitionVisitor 的例子。它在容器启动时解析并替换属性文件中的占位符。
属性编辑器应用: 自定义 BeanDefinitionVisitor 可以用于在bean属性赋值之前应用自定义属性编辑器,进行类型转换或者其他预处理操作。
条件配置: 通过检查 BeanDefinition 的详细信息,BeanDefinitionVisitor 可以实现条件配置,根据不同的环境或条件选择性地修改或激活bean。
实现自定义BeanDefinitionVisitor:
要实现自定义的 BeanDefinitionVisitor,可以继承 BeanDefinitionVisitor 类,并重写 visitXXX 方法来实现特定的访问和修改逻辑。然后在容器启动过程中,或者作为 BeanFactoryPostProcessor 的一部分应用这个访问者到需要的 BeanDefinition 上。
public interface ComputerPart {
public void accept(ComputerPartVisitor computerPartVisitor);
}
ComputerPart 接口定义了 accept 方法,所有具体的计算机部件类将实现这个接口,以允许访问者访问它们。
步骤 2:创建扩展上述类的具体类
public class Keyboard implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
Keyboard 是一个具体的 ComputerPart,实现了 accept 方法,允许访问者访问键盘。
public class Monitor implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
Monitor 是另一个具体的 ComputerPart,实现了 accept 方法,允许访问者访问显示器。
public class Mouse implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
Mouse 是另一个具体的 ComputerPart,实现了 accept 方法,允许访问者访问鼠标。
public class Computer implements ComputerPart {
ComputerPart[] parts;
public Computer(){
parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};
}
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
for (ComputerPart part : parts) {
part.accept(computerPartVisitor);
}
computerPartVisitor.visit(this);
}
}
Computer 是一个复合的 ComputerPart,它包含其他部件。它的 accept 方法首先让访问者访问它的每个部分,然后访问计算机本身。
步骤 3:定义代表访问者的接口
public interface ComputerPartVisitor {
public void visit(Computer computer);
public void visit(Mouse mouse);
public void visit(Keyboard keyboard);
public void visit(Monitor monitor);
}
ComputerPartVisitor 接口定义了对每种类型的 ComputerPart 的访问操作。
步骤 4:创建实现上述类的具体访问者
public class ComputerPartDisplayVisitor implements ComputerPartVisitor {
@Override
public void visit(Computer computer) {
System.out.println("展示电脑。");
}
@Override
public void visit(Mouse mouse) {
System.out.println("展示鼠标。");
}
@Override
public void visit(Keyboard keyboard) {
System.out.println("展示键盘。");
}
@Override
public void visit(Monitor monitor) {
System.out.println("展示显示器。");
}
}
ComputerPartDisplayVisitor 是一个具体的访问者,它定义了如何展示每个部件。
步骤 5:使用 ComputerPartDisplayVisitor 来展示计算机的部件
public class VisitorPatternDemo {
public static void main(String[] args) {
ComputerPart computer = new Computer();
computer.accept(new ComputerPartDisplayVisitor());
}
}
在这个演示类中,我们创建了一个 Computer 对象并用 ComputerPartDisplayVisitor 来展示它的各个部件。
这个示例演示了访问者模式如何使得你可以定义新的操作而无需改变其操作的元素的类。通过这种方式,ComputerPartDisplayVisitor 可以添加新的展示行为而无需修改 ComputerPart 接口或其具体类的任何代码。这使得在不改变已有代码结构的前提下增加新的操作变得容易,从而提高了代码的可维护性和可扩展性。
23种设计模式相关代码后续会逐步提交到github上,方便学习,欢迎指点:
代码地址
https://github.com/RuofeiSun/lf-23Pattern