设计模式:Visitor模式

来源:互联网 发布:类似day one软件 编辑:程序博客网 时间:2024/05/18 12:03

Visitor模式是一个用起来很简单,理解起来可能稍微有一点困难的模式。不过明白了之后就清楚了,其实也是非常的简单。


问题

需要向对象结构中增加新的方法,但是增加起来会很费劲或者会破坏设计。

 

案例

举一个例子。假设我们有一组机器(Windows,Unix,Linux,Mac等),每种机器都有自己不同的底层结构。现在需要在各台机器上装一个软件或者进行一项配置。由于每种操作系统都是不一样的架构,所以做某种配置或者是安装某款软件的手顺都是不同的,而且我们不可能也不想在原有的操作系统中直接添加一个同名的方法来实现这个功能。那么这个时候,Visitor模式就是一个很好的利器了。


还有,最近做的一个工具也是比较适合使用Visitor模式。这个工具是类似于Findbugs的一种功能,要解析一种编程语言的源码文件,然后对其中的内容进行分析,看是否符合某种类型的bug pattern。源码文件首先将被解析成一个数据结构,然后每条规则只需要检查这个数据结构中的一部分,并不需要检查全部的数据结构。对于这种工具,Visitor模式算是再好不过的一种解决方案了。

 

图示

下面两幅类图就是Visitor模式的一种普通的表现形式,它们表示的意思基本上是一致的。

Picture1

Visitor设计模式

Picture2

Visitor设计模式

Picture2和Picture1的区别只是visitor方法的名字不同而已。Picture1中的命名方法可能更常用一些,Picture2中的命名方法则更清晰一些。


以上面那两幅类图中,有一点可能稍微不容易理解,就是一定要在对象结构中的每一个类中增加一个accept(visitor),而且方法体里面又只有一行代码,就只是反调Visitor类。如果这样的话,让Visitor直接访问数据结构不就行了吗?

 

这个问题也让我困扰了一阵子,后来才有点明白,关键点就是使用了双重分派。


Double Dispatch(双重分派,Dual Dispatch)

面向对象的程序中有Overloading(重载)和Overriding(重写)两种概念。

Overriding是指子类重写父类中已有的方法,那么如果一个对象,虽然被申明为父类类型,但是赋值是子类类型,那么在对该对象调用方法时,程序将自动的调用子类中的方法,而回避父类中已有的方法。这一点是在运行时才能够判断的。这个是动态分派。

Overloading是指一个类中,多个同名方法,但是参数不同,有的是父类对象,有的是子类对象。那么在调用这个同名方法时,JVM会自动地根据传入的参数的实际类型而调用不同的方法。这一点是在编译时就已经决定了的。这个就是静态分派。

而Java和C++是动态单分派,静态多分派语言。


Picture1为例,Client中的代码中,elemA.accept(visitor);在判断到底是调用Element还是ConcreateElementA的accept方法时使用的是动态分派;而ConcreateElementA的accept方法中的代码 visitor.visit(this); 在判断到底是调用Visitor类的哪一个visit方法时,是根据this所指代的类型来决定的(也就是参数),这里使用的是静态分派。

 

回答之前那个疑问。如果不使用双重分派,不在对象结构的类中增加accept方法的话,那么对象结构中类之间的关系也就没有意义了。Visitor.visit(elem)这样的方法要能够正确使用的话,就必须把elem声明为具体的类型,如ConcreateElementA,否则程序将直接尝试调用Visitor.visit(Element)方法,而不是Visitor.visit(ConreateElementA)。

 

简单一句话总结,调用方法的主体对象是动态分派的,而调用的方法是先前静态分派好的。


适用对象结构

其实visitor模式比较适用的对象结构是,其中的数据类型分属于不同的类型。如果都已经是同一类型的话,那么直接在对象结构的顶层类中做一些修改,可能就可以达到目的,多见于Composite模式。当然,如果是维护项目的话,不想或者不能修改原有数据结构也是常有的事情,这个时候使用visitor模式也是没有问题的。

依据目的的不同,也可以使用Decorator模式来达成目的。

 

适用情况

1. 需要为一个对象结构添加很多不同的操作

2. 对象结构一般是保持不变的,而操作是经常变化的

3. 操作是针对于对象结构中的具体类的,而不是仅仅针对于高层类

4. 对象结构的内部状态和操作是可见的,或者是原来不可见但是改为可见之后也不会有问题



参考资料

1. http://hi.baidu.com/xinsikao/blog/item/6ad8cbc4ac6189c038db4919.html

2. http://www.cnblogs.com/zhenyulu/articles/81761.html

3. http://www.cnblogs.com/perhaps/archive/2005/08/19/218466.html

4. http://www.cnblogs.com/idior/archive/2005/08/18/217500.html

http://en.wikipedia.org/wiki/Visitor_pattern

http://www.cnblogs.com/idior/archive/2005/01/19/94280.html


Double Dispatch:

http://www.blogjava.net/dreamstone/archive/2006/12/20/88947.html

http://www.eqqon.com/index.php/Double_Dispatch_Mechanism

http://en.wikipedia.org/wiki/Double_dispatch

http://ifacethoughts.net/2006/07/29/single-double-and-multiple-dispatch/


原创粉丝点击