访问者模式(Visitor Pattern):提供一个作用于某对象结构中的各元素的操作表示,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式是一种对象行为型模式。
比如医院的药品处方单,作为多种数据的集合,不同的工作人员需要进行不同的操作。比如划价人员计算价格,药房人员准备药品。
对于这样一个复杂的数据对象,同时存在多个访问者,每个访问者又有不同的操作,可以应用访问者模式来进行设计。
主要角色:
类图:
抽象访问者,对具体元素 A 和 B 的访问:
public interface Visitor {
void visitA(ElementA a);
void visitB(ElementB b);
}
抽象元素,通过 accept 方法接收访问者:
public interface Element {
void accept(Visitor visitor);
}
具体元素 A,除了一些业务方法的实现,还有实现 accpet 方法,调用 visitor 的 visit 方法来访问当前对象:
public class ElementA implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visitA(this);
}
public void sayHello() {
System.out.println("hello A.");
}
public void sayHi() {
System.out.println("hi A.");
}
}
具体访问者 HelloVisitor,实现 visit 方法,调用元素进行业务处理:
public class HelloVisitor implements Visitor {
@Override
public void visitA(ElementA a) {
a.sayHello();
}
@Override
public void visitB(ElementB b) {
b.sayHello();
}
}
元素聚合类 ObjectStructure,为元素的容器,可以传递 Visitor 给对应的元素类:
public class ObjectStructure {
private ElementA elementA;
private ElementB elementB;
public ObjectStructure() {
}
public void addElement(ElementA a) {
this.elementA = a;
}
public void addElement(ElementB b) {
this.elementB = b;
}
public void accept(Visitor visitor) {
elementA.accept(visitor);
elementB.accept(visitor);
}
}
使用的时候,可以很方便的新增或者删除访问者,而无需影响旧代码。
public class TestVisitor {
public static void main(String[] args) {
ObjectStructure objectStructure = new ObjectStructure();
ElementA elementA = new ElementA();
ElementB elementB = new ElementB();
objectStructure.addElement(elementA);
objectStructure.addElement(elementB);
// 第一种访问者
Visitor helloVisitor = new HelloVisitor();
objectStructure.accept(helloVisitor);
// 第二种访问者
Visitor hiVisitor = new HiVisitor();
objectStructure.accept(hiVisitor);
}
}
具体元素对访问者调用的过程也是双重分派的过程:
使用双重分派的好处是,新增访问者不需要修改原本的代码。
应用场景比较有限。
在操作复杂对象,需要不同的访问者来进行不一样的处理,可以考虑使用访问者模式进行改善。
BeanDefinition 为 Spring Bean 的定义信息,在 Spring 解析完配置后,会生成 BeanDefinition 并且记录下来。下次通过 getBean 获取 Bean 的时候,会通过 BeanDefinition 来实例化具体的 Bean 对象。
Spring 的 BeanDefinitionVisitor 用来访问 BeanDefinition。
主要在于解析属性或者构造方法里面的占位符,并且把解析的结果更新到 BeanDefinition 中。
这里就应用的访问者模式。
抽象元素为 BeanDefinition。对 Bean 的定义信息,比如属性值、构造方法参数或者更具体的实现。
具体元素有 RootBeanDefinition、ChildBeanDefinition、GenericBeanDefinition 等等。
这里因为没有对访问者进行扩展,所以只有一个具体访问者 BeanDefinitionVisitor,没有再抽出一层抽象访问者。访问者的访问方法为:
public void visitBeanDefinition(BeanDefinition beanDefinition) {
visitParentName(beanDefinition);
visitBeanClassName(beanDefinition);
visitFactoryBeanName(beanDefinition);
visitFactoryMethodName(beanDefinition);
visitScope(beanDefinition);
visitPropertyValues(beanDefinition.getPropertyValues());
ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
visitIndexedArgumentValues(cas.getIndexedArgumentValues());
visitGenericArgumentValues(cas.getGenericArgumentValues());
}
这里也没有使用双重分派模式,直接调用 visit 进行元素的访问。
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
for (String curName : beanNames) {
if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
try {
visitor.visitBeanDefinition(bd);
}
catch (Exception ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
}
}
}