什么是访问者模式
对象将自己的内部属性暴露给访问者,从而在实现具体功能时,交给访问者。
为什么要用访问者模式
各个具体的对象元素有自己的特异实现,同时针对不同的需求变化,也会有不同的实现方式。
这个时候不同的访问者就相当于不同的需求。所以只需要针对不同的需求定制化不同的访问者,以此来实现不同的需求实现。
而保持对象元素本身不需要改变。
比如,你有个房子需要装修,于是你找了个装修公司A来访问你的房子,A这时就能全面了解你的房子。
此刻需要知道这个房子的装修费用,谁知道呢,房子知道,房子怎么知道呢,因为房子通过访问者A具体的给出的报价知道。
同样的,你有另外一套房子,依然可以邀请访问者A,从而知道具体装修报价。这样看来是不是可以针对不同的对象元素,在访问者里集中处理需要实现的那个具体功能。映射到这个例子就是访问者A能知道集中处理你所有房子的装修报价。
这里,是不是将报价这个功能实现从房子这个对象元素本身中剥离开了,代价也很明显,访问者需要深入对象元素本身,打破了对象的封闭原则。
那为什么这里不在元素本身实现报价这个依赖对象本身具体属性的方法呢。因为可预见的需求是多变的。
换言之,为什么不再找家装修公司来看看呢,货比三家再做打算咯。于是,访问者B来了。只需要在访问者B里实现另外一套逻辑,然后,将访问者B邀请到你的房子里。
你现在看看,房子的报价,是不是访问者B给出的。
访问者模式组成
对象元素
public abstract class Element {
public abstract void doSomething();
public abstract void accept(IVisitor visitor);
}
具体对象元素(相对于你的两套房)
public class ElementImp1 extends Element{
@Override
public void doSomething() {
System.out.println("el1 do something");
}
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
public class ElementImp2 extends Element{
@Override
public void doSomething() {
System.out.println("el2 do something");
}
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
访问者接口
public interface IVisitor {
void visit(ElementImp1 element);
void visit(ElementImp2 element);
}
访问者实现
public class Visitor implements IVisitor {
@Override
public void visit(ElementImp1 element) {
element.doSomething();
}
@Override
public void visit(ElementImp2 element) {
element.doSomething();
System.out.println("el2 do next");
}
}
测试驱动
public class Main {
public static void main(String[] args) {
IVisitor visitor = new Visitor();
Element el1 = new ElementImp1();
el1.accept(visitor);
Element el2 = new ElementImp2();
el2.accept(visitor);
}
}
访问者模式的缺点
缺点似乎也很明显,首先看到因为访问者要深入对象元素,访问者需要面向实现编程,即例子代码中Visitor的visit方法接收的是具体实现类。
不能面向接口的原因是,访问者对不同的对象元素需要做不同的事情,如例子中针对ElementImp2要的和ElementImp1就不一样。所以需要靠具体实现来区分。
因为访问者深入对象元素的具体实现里,这样也就带来另外一个问题
一旦对象元素需要改动,将可能会影响到访问者修改具体访问实现代码,如果有多个访问者,可能会涉及多处的改动。