本节使用訪问者模式的基本结构,讨论它的作用和意义,或者说意图。
1.问题的提出
现有这样一个方案:Person有子类Boy、Girl;Person定义了方法say()、eat(int i)、walk()。
package method.visitor; public abstract class Person{ public abstract void accept(Visitor v); /* public abstract String say(); public abstract int eat(int i);//长肉 public abstract void walk(); */ }
Person的这些代码,由其子类实现。Person很easy扩展出新的子类。
可是,
- (据说eat与身高、心情、减肥等相关,有很复杂的算法...)据说say()与walk()的代码放在一起,会显得不专业
- 因为Person的这些方法的(实现)代码将分散到Boy、Gril中“导致整个系统难以理解”。
- 更重要的是,Person增添新的方法困难——违反OCP、整个类层次须要改动。
于是。有了訪问者模式。从结构出发,訪问者模式2中好人打贱人说明了一个事实。仅仅要涉及双分派,就能够使用訪问者模式。
2.专项检查
GoF的訪问者模式的入手点:将Person的层次横切为Visitor层次。
- Person定义的方法say()、eat(int i)、walk()将被封装为訪问子类。加入一个accept()。
- Visitor及其子类将对Person的子类进行訪问。
訪问者是做专项检查的。
Person有n个子类m个方法。转换为Visitor则有m个子类n个方法。尽管n*m=m*n。
訪问者模式的最好使用场合:m越大n越小越好。
换言之:訪问者类型非常多而对象结构(如Person)中元素(或子类)类型非常少,这才适合訪问者模式。
package method.visitor; /** * @author yqj2065 * @version 2014.9 */ public abstract class Visitor{ public abstract void visit(Person p); abstract void visitBoy(); abstract void visitGirl(); } package method.visitor; import static tool.Print.*; public class SayVisitor extends Visitor{ public void visit(Person p){ p.accept(this); } @Override void visitBoy(){ //Boy.say pln("Boy.say"); } @Override void visitGirl(){ pln("Girl.say"); } }//EatVisitor、WalkVisitor略所以,(Visitor或)EatVisitor表示“一个作用于某对象结构中的各元素的操作”,EatVisitor表示Person的eat(int i);
所以。Visitor“使你能够在不改变各元素的类的前提下定义作用于这些元素的新操作”。比如。希望为Person增添song()操作,加入一个SongVisitor就可以。
如今。能够easy地为Person(通过扩展Visitor)增添新操作。可是Person本身的扩展,因为Visitor的牵绊,非常困难。
3.对象结构
GoF的訪问者模式中,包括了一个用于存储元素对象集合的对象结构。在讨论訪问者模式时。个人从来不关注它。它没必要的。但却是常见的、能够理解的。整体而言。我们在学习訪问者模式时,能够将“对象结构”省略掉。
(假设你认为有这个对象结构,使得你更easy理解訪问者模式,你能够带上它)
这个对象结构,能够是不论什么数据结构,这里演示的Group有一个最常见的ArrayList<Person>保存各种详细元素(Person的各种子类)。能够使用组合模式构造更复杂的数据结构来组织元素,比如将Group设计为Person的子类,等等。
可是全部这些和訪问者模式并没有实际的关系。
例程 3-31对象结构 package method.visitor; import java.util.ArrayList; public class Group{ private ArrayList<Person> list=new ArrayList<>(); public void accept(Visitor visitor){ for(Person x :list){ x.accept(visitor); } } public void add(Person product){ list.add(product); } public void remove(Person product) { list.remove(product); } }如果某个观察者Visitor v1要观察一个小团队Group中的各成员怎样说话。Test的代码能够例如以下:
public static void testGroup(){
Visitor v1 =(Visitor)God.create("3-5-Visitor1");//SayVisitor
// Visitor v2 =(Visitor)God.create("3-5-Visitor2");// new EatVisitor();
// Visitor v3 =(Visitor)God.create("3-5-Visitor3");//new WalkVisitor();
Person a =(Person)God.create("3-5-Person1");// new Boy();
Person b =(Person)God.create("3-5-Person1");// new Boy();
Person c =(Person)God.create("3-5-Person2");// new Girl();
Group g = new Group();
g.add(a);g.add(b);g.add(c);
g.accept(v1);//g.accept(v2);g.accept(v3);
}
依照配置文件。其输出的一例:
Boy.say
Boy.say
Girl.say