Visitor类及其子类
我们使用术语访问者(v i s i t o r)来泛指在遍历过程中"访问"被遍历对象并做适当操作的一类对象。本例中我们使用一个Vi s i t o r类来定义一个访问结构中图元的接口。

访问者的具体子类做不同的分析,例如,我们可以用一个S p e l l i n g C h e c k i n g Vi s i t o r子类来检查拼写;用H y p h e n a t i o n Vi s i t o r子类做连字符分析。S p e l l i n g C h e c k i n g Vi s i t o r可以就像我们上
面的S p e l l i n g C h e c k e r那样来实现,只是操作名要反映通用的访问者的类接口。例如,C h e c k C h a r a c t e r应该改成Vi s i t C h a r a c t e r。
既然C h e c k M e对于访问者并不合适,因为访问者不检查任何东西。故我们要使用一个更加通用的名字: A c c e p t,其参数也应该改成Vi s i t o r &,以反映它能接受任何一个访问者这一事实。现在定义一个新的分析只需要定义一个新的Vi s i t o r子类---我们无需触及任何图元类。通
过在G l y p h及其子类中增加这一操作,我们就可以支持以后的所有分析方法。
我们已经看到怎样做拼写检查了。我们可以在H y p h e n a t i o n Vi s t i t o r中使用类似的方法来累积文本,但一旦H y p h e n a t i o n Vi s i t o r的Vi s i t C h a r a c t e r操作用于处理整个单词,它的工作方式将略有不同。它并不是检查单词的拼写错误,而是使用一个连字符算法决定单词可能的连字符点的位置( 如果有的话)。然后在每一个连字符点,插入一个D i s c r e t i o n a r y 图元。
D i s c r e t i o n a r y图元是G l y p h子类D i s c r e t i o n a r y的实例。一个D i s c r e t i o n a r y图元有两种可能的外观,这决定于它是否是一行的最后一个字符。如果它是最后一个字符,那么D i s c r e t i o n a r y看起来像一个连字符;如果不是,那么D i s c r e t i o n a r y不显示任何东西。D i s c r e t i o n a r y检查它的父对象(一个行对象)来判断它是否是最后的子女。D i s c r e t i o n a r y在每次被激活画自己或计算它的边界时,都要作这个检查。格式化策略将D i s c r e t i o n a r y看成空格,将它们都作为行结束的标志。下图说明了一个嵌入的D i s c r e t i o n a r y是怎样显示的。

Visitor模式
我们这里所描述的是Vi s i t o r模式的一个应用。前面的Vi s i t o r类及其子类是该模式的主要参与者。Vi s i t o r模式记述了这样一种我们前面已使用过的技术,它允许对图元结构所作分析的数目不受限制地增加而不必改变图元类本身。访问者类的另一个优点是它不局限使用于像图元结构这样的组合者,也适用于其他任何对象结构。包括集合、列表,甚至无环有向图。再者,访问者所能访问的类之间无需通过一个公共父类关联起来。也就是说,访问者能跨越类层次结构。
在使用Vi s i t o r模式之前你要问自己的一个重要问题是:哪一个类层次变化得最厉害?该模式最适合于当你想对一个稳定类结构的对象做许多不同的事情的情况。增加一种新的访问者而不需要改变类结构,这对于很大的类结构是尤其重要的。但是,只要你给类结构增加了一个子类,你就不得不更新你所有访问者类的接口以包含针对那个子类的Vi s i t . . .操作。比如,在我们的例子中,增加一个被称为F o o的新G l y p h子类,将需要改变Vi s i t o r及其子类,以包含一个Vi s i t F o o操作。但是考虑到我们的设计限制条件,我们比较多的是为L e x i增加一种新的分析方法,而不是增加一种新的图元。所以Vi s i t o r模式是适合我们的需要的。
小结
我们在L e x i的设计中使用了8种不同的模式:
-
Composite表示文档的物理结构。
-
Strategy允许不同的格式化算法。
-
Decorator修饰用户界面。
-
Abstract Factory支持多视感标准。
-
Bridge允许多个窗口平台。
-
Command支持撤销用户操作。
-
Iterator访问和遍历对象结构。
-
Vi s i t o r允许无限扩充分析能力而又不会使文档结构的实现复杂化。
以上这些设计要点都不仅仅局限于像L e x i这样的文档编辑应用。事实上,很多重要的应用都可以使用这些模式处理不同的事情。一个财务分析应用可能使用C o m p o s i t e定义由多种类型子文件夹组成的投资文件夹。一个编译程序可能使用S t r a t e g y模式来考虑不同目标机上的寄存器分配方案。图形界面的应用可能是至少要用到D e c o r a t o r和C o m m a n d模式,正如本例所示。
我们已经涉及到了L e x i设计中的一些主要问题,但还有很多其他的问题我们没有讨论。需再次说明的是,本书描述的不仅是以上我们所用到的8个模式。所以在学习其余模式时,你要考虑怎样才能把它们用在L e x i中。最好能考虑在你自己的设计中怎样使用它们。