访问者模式是对象的行为模式。访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。
主要角色
抽象访问者:抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法中的参数定义哪些对象是可以被访问的。
访问者:实现抽象访问者所声明的方法,它影响到访问者访问到一个类后该干什么,要做什么事情。
抽象元素类:接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。抽象元素一般有两类方法,一部分是本身的业务逻辑,另外就是允许接收哪类访问者来访问。
元素类:实现抽象元素类所声明的accept方法,通常都是visitor.visit(this),基本上已经形成一种定式了。
结构对象:一个元素的容器,一般包含一个容纳多个不同类、不同接口的容器,如List、Set、Map等,在项目中一般很少抽象出这个角色。
适用性
1、一个对象结构内部包含很多实现了不同接口的类对象,你想对这些对象执行依赖于其具体类的操作。
2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。访问者模式使得你可以将相关的操作集中起来定义在一个类中,当该对象结构被很多应用共享时,用访问者模式让每个应用仅包含需要用到的操作。
3、定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
优点
1、访问者模式使得增加新的操作变得很容易。如果一些操作依赖于一个复杂的结构对象的话,那么一般而言,增加新的操作会很复杂。而使用访问者模式,增加新的操作就意味着增加一个新的
2、访问者类,因此,变得很容易。
3、访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。
4、访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。迭代子只能访问属于同一个类型等级结构的成员对象,而不能访问属于不同等级结构的对象。访问者模式可以做到这一点。
5、积累状态。每一个单独的访问者对象都集中了相关的行为,从而也就可以在访问的过程中将执行操作的状态积累在自己内部,而不是分散到很多的节点对象中。这是有益于系统维护的优点。
缺点
1、增加新的节点类变得很困难。每增加一个新的节点都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作。
2、破坏封装。访问者模式要求访问者对象访问并调用每一个节点对象的操作,这隐含了一个对所有节点对象的要求:它们必须暴露一些自己的操作和内部状态。不然,访问者的访问就变得没有意义。由于访问者对象自己会积累访问操作所需的状态,从而使这些状态不再存储在节点对象中,这也是破坏封装的。
实现
网上的资料中汽车的例子,实现访问者模式的一个小例子。类图如下:
IPart(被访问者的抽象定义)的定义:
package com.lidaming.design20.visitor; public interface IPart { void accept(IWorker worker); }
Engine(具体被访问者)的实现:
package com.lidaming.design20.visitor; public class Engine implements IPart { public void accept(IWorker worker) { worker.visit(this); } }
Wheel实现:
package com.lidaming.design20.visitor; public class Wheel implements IPart { public void accept(IWorker worker) { worker.visit(this); } }
Car的实现:
package com.lidaming.design20.visitor; public class Car implements IPart { private Engine engine = new Engine(); private Wheel wheel = new Wheel(); public void accept(IWorker worker) { wheel.accept(worker); engine.accept(worker); worker.visit(this); } }
IWorker(访问者的抽象)的定义:
package com.lidaming.design20.visitor; public interface IWorker { void visit(Engine e); void visit(Wheel e); void visit(Car e); }
Engineer(具体访问者)的实现:
package com.lidaming.design20.visitor; public class Engineer implements IWorker { public void visit(Engine e) { System.out.println("check engine..."); } public void visit(Wheel e) { System.out.println("check wheel..."); } public void visit(Car e) { System.out.println("check car ..."); } }
场景类的实现:
package com.lidaming.design20.visitor; public class Client { public static void main(String[] args) { Engineer engineer = new Engineer(); Car car = new Car(); car.accept(engineer); } }
延伸
仔细阅读会发现,代码中存在相互引用(环引用),会产生无效依赖,甚至OOM,要解决这个问题,就要具体实现不同的Engineer,在Car中进行组合,只对抽象操作。
参考
http://www.cnblogs.com/qicosmos/p/3357178.html
http://www.cnblogs.com/java-my-life/archive/2012/06/14/2545381.html
http://www.cnblogs.com/singlepine/archive/2005/10/30/265025.html