25.2 解 决 方 案
25.2.1 使用访问者模式来解决问题
用来解决上述问题的一个合理的解决方案,就是使用访问者模式。那么什么是访问者模式呢?
(1)访问者模式的定义
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
(2)应用访问者模式来解决的思路
仔细分析上面的示例。对于客户这个对象结构,不想改变类,又要添加新的功能,很明显就需要一种动态的方式,在运行期间把功能动态地添加到对象结构中去。
有些朋友可能会想起装饰模式,装饰模式可以实现为一个对象透明地添加功能,但装饰模式基本上是在现有功能的基础之上进行功能添加,实际上是对现有功能的加强或者改造,并不是在现有功能不改动的情况下,为对象添加新的功能。
看来需要另外寻找新的解决方式了,可以应用访问者模式来解决这个问题。访问者模式实现的基本思路如下。
首先定义一个接口来代表要新加入的功能,为了通用,也就是定义一个通用的功能方法来代表新加入的功能。
在对象结构上添加一个方法,作为通用的功能方法,也就是可以代表被添加的功能,在这个方法中传入具体的实现新功能的对象。
在对象结构的具体实现对象中实现这个方法,回调传入具体的实现新功能的对象,就相当于调用到新功能上了。
接下来的步骤就是提供实现新功能的对象。
最后再提供一个能够循环访问整个对象结构的类,让这个类来提供符合客户端业务需求的方法,来满足客户端调用的需要。
这样一来,只要提供实现新功能的对象给对象结构,就可以为这些对象添加新的功能,由于在对象结构中定义的方法是通用的功能方法,所以什么新功能都可以加入。
25.2.2 访问者模式的结构和说明
访问者模式的结构如图25.3所示。
图25.3 访问者模式结构示意图
■ Visitor:访问者接口,为所有的访问者对象声明一个visit方法,用来代表为对象结构添加的功能,理论上可以代表任意的功能。
■ ConcreteVisitor:具体的访问者实现对象,实现要真正被添加到对象结构中的功能。
■ Element:抽象的元素对象,对象结构的顶层接口,定义接受访问的操作。
■ ConcreteElement:具体元素对象,对象结构中具体的对象,也是被访问的对象,通常会回调访问者的真实功能,同时开放自身的数据供访问者使用。
■ ObjectStructure:对象结构,通常包含多个被访问的对象,它可以遍历多个被访问的对象,也可以让访问者访问它的元素。可以是一个复合或是一个集合,如一个列表或无序集合。
注意
但是请注意:这个ObjectStructure并不是我们在前面讲到的对象结构,前面一直讲的对象结构是指的一系列对象的定义结构,是概念上的东西;而ObjectStructure可以看成是对象结构中的一系列对象的一个集合,是用来辅助客户端访问这一系列对象的。为了不造成大家的困惑,所以后面提到ObjectStructure的时候,就用英文名称来代替,不把它翻译成中文。
25.2.3 访问者模式示例代码
(1)首先需要定义一个接口来代表要新加入的功能,把它称作访问者,访问谁呢?当然是访问对象结构中的对象了。既然是访问,不能空手而去吧,这些访问者在进行访问的时候,就会携带新的功能。也就是说,访问者携带着需要添加的新的功能去访问对象结构中的对象,就相当于给对象结构中的对象添加了新的功能。示例代码如下:
(2)然后看看抽象的元素对象定义。示例代码如下:
(3)再来看看元素对象的具体实现。
先看看元素A的实现。示例代码如下:
再看看元素B的实现。示例代码如下:
(4)接下来看看访问者的具体实现。
先看看访问者1的实现。示例代码如下:
访问者2的实现和访问者1的示意代码是一样的,就不再赘述。
(5)该来看看ObjectStructure的实现了。示例代码如下:
(6)最后来看看客户端的示意实现。示例代码如下:
25.2.4 使用访问者模式重写示例
要使用访问者模式来重写示例,首先就要按照访问者模式的结构,分离出两个类层次来,一个是对应于元素的类层次,一个是对应于访问者的类层次。
对于对应于元素的类层次,现在已经有了,就是客户的对象层次。而对应于访问者的类层次,现在还没有,不过,按照访问者模式的结构,应该是先定义一个访问者接口,然后把每种业务实现成为一个单独的访问者对象,也就是说应该使用一个访问者对象来实现对客户的偏好分析,而用另外一个访问者对象来实现对客户的价值分析。
在分离好两个类层次以后,为了方便客户端的访问,定义一个ObjectStructure,其实就类似于前面示例中客户管理的业务对象。新的示例的结构如图25.4所示。
图25.4 使用访问者模式的示例程序结构示意图
仔细查看图25.4所示的程序结构示意图,细心的朋友会发现,在图上没有出现对客户进行价值分析的功能了。这是为了示范“使用访问者模式来实现示例功能以后,可以很容易地给对象结构增加新的功能”,所以先不做这个功能,等都实现好了,再来扩展这个功能。接下来还是看看代码实现,以更好地体会访问者模式。
(1)先来看看Customer的代码,Customer相当于访问者模式中的Element,它的实现和以前相比有以下改变。
■ 新增一个接受访问者访问的方法。
■ 把能够分离出去放到访问者中实现的方法,从Customer中删除,包括:客户提出服务请求的方法、对客户进行偏好分析的方法、对客户进行价值分析的方法等。
示例代码如下:
(2)下面来看看两种客户的实现。先来看看企业客户的实现。示例代码如下:
再来看看个人客户的实现。示例代码如下:
(3)下面来看看访问者的接口定义。示例代码如下:
(4)下面来看看各个访问者的实现,每个访问者对象负责一类的功能处理。先来看看实现客户提出服务请求功能的访问者。示例代码如下:
接下来看看实现对客户偏好分析功能的访问者。示例代码如下:
(5)下面来看看ObjectStructure的实现。示例代码如下:
(6)该来写个客户端测试一下了。示例代码如下:
运行结果如下:
使用访问者模式重新实现了前面示例的功能,把各类相同的功能放在单独的访问者对象中,使得代码不再杂乱,系统结构也更清晰,能方便地维护了,解决了前面示例的一个问题。
还有一个问题,就是看看能不能方便地增加新的功能,前面在示例的时候,故意留下了一个对客户进行价值分析的功能没有实现,那么接下来就看看如何把这个功能增加到已有的系统中。在访问者模式中要给对象结构增加新的功能,只需要把新的功能实现成为访问者,然后在客户端调用的时候使用这个访问者对象来访问对象结构即可。
下面来看看实现对客户价值分析功能的访问者。示例代码如下:
使用这个功能,只要在客户端添加以下代码即可。示例代码如下: