访问者模式Visitor
表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
有某个操作,它是作用于一些元素之上的,而这些元素属于某一个对象结构。
Visitor:抽象访问者: 其visit方法限定具体访问者能访问的类
ConcreteVisitor:具体访问者,实现visit方法,访问后对元素类对象要做的操作
Element:抽象元素类,主要为子类提供接受Visitor的功能
ConcreteElement:具体元素类,实现Accept方法,格式一般为visitor.visit(this),还拥有各自的特殊方法
ObjectStructure:元素容器,持有元素对象引用集,可以提供公共方法使访问者访问引用集中的元素,一般不使用(并不非要遍历),他与这些类再没有其他关系
利用Visitor增加新的操作,增加新的行为,可以是扩展其能力,也可以利用其属性进行某些操作
“伪动态双分派”
优缺点
优点:
可扩展,只需要增加新的ConcreteVisitor,就可对数据进行新的操作
低耦合,数据结构和作用于数据结构的操作分离
缺点:
破坏封装:Visitor能使用ConcreteElement中的方法,不符合迪莫特原则
不符合依赖倒置原则,因ConcreteVisitor要对每个元素进行不同操作,需要持有ConcreteElement
一旦增加一个ConcreteElement,那么Visitor都要随着改变
使用场景
本身使用很少,很难有一个类中的数据结构不变化
1.系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的
2,当一个数据结构中,一些元素类需要负责与其不相关的操作的时候,为了将这些操作分离出去,以减少这些元素类的职责时,可以使用访问者模式。
3.有时在对数据结构上的元素进行操作的时候,需要区分具体的类型,这时使用访问者模式可以针对不同的类型,在访问者类中定义不同的操作,从而去除掉类型判断。
访问者模式适用于数据结构相对稳定的系统,把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作可以相对自由的演化。
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。
伪动态双分派
Element.accept(visitor);,所有的Element接收的方法的类型都是visitor类型,方法的参数类型不会影响虚拟机对方法的选择,虚拟机具体是调用ElementA的accept()方法还是调用ElementB的accept()方法,是由Element的实际的类型来决定的,在此完成了一次动态单分派
在运行时根据Visitor的具体类型来选择this所对应的visit(ElementA element)方法还是调用visit(ElementB element)方法,在此完成了一次动态的单分派
两次动态单分派结合起来,就完成了一次伪动态双分派,先确定了调用哪个Element的accept()方法,然后再确定了调用那个Visitor中的针对this对应的具体Element的visit()方法,这就是伪动态双分派
在accept的方法实现中,传递this进去 具体的visitor根据this的类型,又完成了一次分派,找到了需要调用的方法
实例
Element:
public abstract class Element {
public abstract void accept(Visitor visitor);
public void operation(){
System.out.println("共有方法");
}
}
ConcreteElement:
public class ConcreteElement1 extends Element {
@Override
public void accept(Visitor visitor) {
visitor.visit1(this);
}
public void operation1(){
System.out.println("特有方法一");
}
}
public class ConcreteElement2 extends Element {
@Override
public void accept(Visitor visitor) {
visitor.visit2(this);
}
public void operation2(){
System.out.println("特有方法二");
}
}
Vistor
public interface Visitor {
void visit1(ConcreteElement1 ce1);
void visit2(ConcreteElement2 ce2);
}
ConcreteVisitor
public class ConcreteVisitor implements Visitor {
@Override
public void visit1(ConcreteElement1 ce1) {
ce1.operation1();
}
@Override
public void visit2(ConcreteElement2 ce2) {
ce2.operation2();
}
}
public class ConcreteVisitor2 implements Visitor{
@Override
public void visit1(ConcreteElement1 ce1) {
ce1.operation();
ce1.operation1();
}
@Override
public void visit2(ConcreteElement2 ce2) {
ce2.operation2();
ce2.operation2();
}
}
ObjectStructure
public class ObjectStructure {
List<Element> list = new ArrayList<>();
{
list.add(new ConcreteElement1());
list.add(new ConcreteElement2());
}
public void acceptVisitor(Visitor visitor){
for (Element element : list) {
element.accept(visitor);
}
}
}
测试
public class Test {
public static void main(String[] args) {
ObjectStructure os = new ObjectStructure();
Visitor visitor1 = new ConcreteVisitor();
Visitor visitor2 = new ConcreteVisitor2();
os.acceptVisitor(visitor1);
System.out.println("-----------------------");
os.acceptVisitor(visitor2);
}
}