Visitor(访问者)---对象行为型模式
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
?
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作比较好。
?
一个使用Visitor模式的客户必须创建一个ConcreteVisitor对象,然后遍历该对象结构,并用该访问者访问每一个元素。
当一个元素被访问时,它调用对应于它的类的Visitor操作。如果必要,该元素将自身作为这个操作的一个参数以便该访问者访问它的状态。
?
访问者模式的优点和缺点:
1、访问者模式使得易于增加新的操作。访问者使得增加依赖于复杂对象结构的构件的操作变得容易了。仅需增加一个新的访问者即可在一个对象结构上定义一个新的操作。相反,如果每个功能都分散在多个类之上的话,定义新的操作时必须修改每个类。
2、访问者集中相关的操作而分离无关的操作。相关的行为不是分布在定义该对象结构的各个类上,而是集中在一个访问者中。无关行为却被分别放在它们各自的访问者子类中。这就既简化了这些元素的类,也简化了在这些访问者中定义的算法。所有与它的算法相关的数据结构都可以被隐藏在访问者中。
3、增加新的ConcreteElement类很困难。Visitor模式使得难以增加新的Element的子类。每添加一个新的ConcreteElement都要在Visitor中添加一个新的抽象操作,并在每一个ConcreteVisitor类中实现相应的操作。有时可以在Visitor中提供一个缺省的实现,这一实现可以被大多数的ConcreteVisitor继承,但这与其说是一个规律还不如说是一种例外。
所以在应用访问者模式时,考虑关键的问题是系统的哪个部分会经常变化,是作用于对象结构上的算法呢,还是构成该结构的各个对象的类。如果老是有新的ConcreteElement类加入进来的话,Visitor类层次将变得难以维护。在这种情况下,直接在构成该结构的类中定义这些操作可能更容易一些。如果Element类层次是稳定的,而你不断地增加操作或修改算法,访问者模式可以帮助你管理这些改动。
4、破坏封装。访问者方法假定ConcreteElement接口的功能足够强,足以让访问者进行它们的工作。结果是,该模式常常迫使你提供访问元素内部状态的公共操作,这可能会破坏它的封装性。
?
每一个对象结构将有一个相关的Visitor类。这个抽象的访问者类为定义对象结构的每一个ConcreteElement类声明一个VisitConcreteElement操作。每一个Visitor上的Visit操作声明它的参数为一个特定的ConcreteElement,以允许该Visitor直接访问ConcreteElement的接口。ConcreteVisitor类重定义每一个Visit操作,从而为相应的ConcreteElement类实现与特定访问者相关的行为。
?
一个访问者必须访问这个对象结构的每一个元素。我们可以将遍历的责任放到下面三个地方中的任意一个:对象结构中,访问者中,或一个独立的迭代器对象中。
通常由对象结构负责迭代。一个集合只需对它的元素进行迭代,并对每一个元素调用Accept操作。而一个复合通常让Accept操作遍历该元素的各子构件并对它们中的每一个递归地调用Accept。
另一个解决方案是使用一个迭代器来访问各个元素。
甚至可以将遍历算法放在访问者中,尽管这样将导致对每一个聚合ConcreteElement,在每一个ConcreteVisitor中都要复制遍历的代码。将该遍历策略放在访问者中的主要原因是想实现一个特别复杂的遍历,它依赖于对该对象结构的操作结果。