访问者模式
访问者模式的目的是封装一些施加于某种数据结构元素之上的操作,一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。
访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。数据结构的每一个节点都可以接受一个访问者的调用,此节点向访问者对象传入节点对象,而访问者对象则反过来执行节点对象的操作。这样的过程叫做"双重分派"。节点调用访问者,将它自己传入,访问者则将某算法针对此节点执行。
访问者模式结构:
抽象访问者角色:声明了一个或者多个访问操作,形成所有的具体元素角色必须实现的接口
具体访问者角色:实现抽象访问者角色所声明的接口,也就是抽象访问者所声明的各个访问操作
抽象节点角色:声明一个接受操作,接受一个访问者对象作为一个参量
具体节点角色:实现了抽象元素所规定的接受操作
结构对象角色:可以遍历结构中的所有元素,提供一个高层次的接口让访问者对象可以访问每一个元素,可以设计成一个符合对象或者一个聚集
public interface Person {
void accept(Visitor visitor);
}
public class Woman implements Person{
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public class Man implements Person{
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public interface Visitor {
public void visit(Man man);
public void visit(Woman girl);
}
//成功时Man与Woman的不同表现
public class Success implements Visitor{
public void visit(Man man) {
System.out.println("当男人成功时,背后多半有一个伟大的女人");
}
public void visit(Woman woman) {
System.out.println("当女人成功时,背后大多有一个不成功的男人");
}
}
//恋爱时Man与Woman的不同表现
public class Love implements Visitor{
public void visit(Man man) {
System.out.println("当男人恋爱时,凡事不懂也装懂");
}
public void visit(Woman girl) {
System.out.println("当女人恋爱时,遇事懂也装不懂");
}
}
结构对象的代码
public class ObjectStructure {
private List<Person> elements = new ArrayList<Person>();
public void attach(Person element){
elements.add(element);
}
public void detach(Person element){
elements.remove(elements);
}
//遍历各种具体元素并执行他们的accept方法
public void display(Visitor visitor){
for(Person p:elements){
p.accept(visitor);
}
}
}
public class Client {
public static void main(String[] args) {
ObjectStructure o = new ObjectStructure(); //依赖于ObjectStructure
//实例化具体元素
o.attach(new Man());
o.attach(new Woman());
//当成功时不同元素的不同反映
Visitor success = new Success(); //依赖于抽象的Visitor接口
o.display(success);
//当恋爱时的不同反映
Visitor amativeness = new Love(); //依赖于抽象的Visitor接口
o.display(amativeness);
}
}
优缺点:
优点
1、使得增加新的访问操作变得很容易。
2、将有关元素对象的访问行为集中到一个访问者对象中,而不是分散到一个个的元素类中。
3、可以跨过类的等级结构访问属于不同的等级结构的元素类。
4、让用户能够在不修改现有类层次结构的情况下,定义该类层次结构的操作。
缺点:
1、增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,违背了“开闭原则”的要求。
2、破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法供访问者访问。
使用范围:
1、一个对象结构包含很多类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作。
2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。访问者模式使得我们可以将相关的访问操作集中起来定义在访问者类中,对象结构可以被多个不同的访问者类所使用,将对象本身与对象的访问操作分离。
3、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
模式扩展:
1、由于访问者模式需要对对象结构进行操作,而对象结构本身是一个元素对象的集合,因此访问者模式经常需要与迭代器模式联用,在对象结构中使用迭代器来遍历元素对象。
2、在访问者模式中,元素对象可能存在容器对象和叶子对象,因此可以结合组合模式来进行设计。
倾斜的“开闭原则”
访问者模式以一种倾斜的方式支持“开闭原则”,增加新的访问者方便,但是增加新的元素很困难。