访问者(Visitor)模式:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。访问者模式的结构图如下:
通过上图可以看到他有如下角色:
抽象访问者(Visitor)角色:定义接口,声明一个或多个访问操作。
具体访问者(ConcreteVisitor)角色:实现抽象访问者所声明的接口,也就是抽象访问者所声明的各个访问操作。
抽象元素(Visitable)角色:声明一个接受操作,接受一个访问者对象作为一个参数。
具体元素结点(ConcreteElement)角色:实现抽象结点所规定的接受操作。
数据结构对象(ObjectStructure)角色:可以遍历结构中的所有元素,提供一个接口让访问者对象都可以访问每一个元素。
模拟代码如下:
1
2
3
4
5
|
interface Visitor { void visit(Gladiolus g); void visit(Chrysanthemum c); } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// concrete visitor 名称访问 class StringVisitor implements Visitor { String s; public String toString() { return s; } public void visit(Gladiolus g) { s = "Gladiolus" ; } public void visit(Chrysanthemum c) { s = "Chrysanthemum" ; } } |
1
2
3
4
5
6
7
8
9
10
11
|
// concrete visitor 蜜蜂访问 class BeeVisitor implements Visitor { public void visit(Gladiolus g) { System.out.println( "蜜蜂 来 访问 Gladiolus" ); } public void visit(Chrysanthemum c) { System.out.println( "蜜蜂 来 访问 Chrysanthemum" ); } } |
1
2
3
|
interface Flower { void accept(Visitor v); } |
1
2
3
4
5
6
7
8
|
/* * concrete element 菊花 */ class Chrysanthemum implements Flower { public void accept(Visitor v) { v.visit( this ); } } |
1
2
3
4
5
6
|
// concrete element 剑兰 class Gladiolus implements Flower { public void accept(Visitor v) { v.visit( this ); } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//这是Flower一个对象生成器 class FlowerGenerator { private static Random rand = new Random(); public static Flower newFlower() { switch (rand.nextInt( 2 )) { default : case 0 : return new Gladiolus(); case 1 : return new Chrysanthemum(); } } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public class Test { /* * 首先在客户端先获得一个具体的访问者角色 遍历对象结构 对每一个元素调用accept方法,将具体访问者角色传入 这样就完成了整个过程 */ public static void main(String args[]) { List<Flower> flowers = new ArrayList<Flower>(); for (int i = 0; i < 10; i++) flowers.add(FlowerGenerator.newFlower()); Visitor visitor = new StringVisitor(); Iterator<Flower> iterator = flowers.iterator(); while (iterator.hasNext()) { iterator.next().accept(visitor); System.out.println(visitor); } System.out.println("---------------"); /* * 一个新的访问行为 :BeeVisitor 蜜蜂访问 */ Visitor visitor2 = new BeeVisitor(); for (Flower flower : flowers) { flower.accept(visitor2); } } } |
结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
Gladiolus Chrysanthemum Chrysanthemum Gladiolus Chrysanthemum Chrysanthemum Chrysanthemum Chrysanthemum Gladiolus Gladiolus --------------- 蜜蜂 来 访问 Gladiolus 蜜蜂 来 访问 Chrysanthemum 蜜蜂 来 访问 Chrysanthemum 蜜蜂 来 访问 Gladiolus 蜜蜂 来 访问 Chrysanthemum 蜜蜂 来 访问 Chrysanthemum 蜜蜂 来 访问 Chrysanthemum 蜜蜂 来 访问 Chrysanthemum 蜜蜂 来 访问 Gladiolus 蜜蜂 来 访问 Gladiolus |
有以下情形可以考虑使用访问者模式:
1、一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。
3、当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
4、 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
这些个人看来都是建议,项目中还要具体问题具体分析了。