Skip to content

Latest commit

 

History

History
65 lines (44 loc) · 3.8 KB

访问者.md

File metadata and controls

65 lines (44 loc) · 3.8 KB

访问者模式 Visitor

背景

例如对于男人和女人,都可以去美发,但具体实现是不同。如果分别对男人和女人做头发,那么成本又高又浪费资源,不如开家美发店,同时提供男人和女人的美发服务。

定义

表示一个作用于[1]某对象结构中各元素的操作,使程序员可以在[2]不改变各元素的类的前提下定义作用于这些元素的新操作。

难点 & 关键

  1. 作用于对象结构中的各元素

    • 不同的元素结点,但都源于同一个接口(例如男女都是人), —— 结点类Node。

    • 可以从中抽象出相同的操作,封装为访问者类Visitor。

    • 需要遍历所有结点

    • 十分抵触对象结构本身的扩展

  2. 不改变各元素的类

    • 结点类中有接受访问者对象的方法Accept。

    • 访问者类中有某种方法对所有具体结点的实现VisitConcreteNode'X'。

  3. 谁负责遍历结点?

    • 三个地方: 对象结构中,访问者中,一个独立的迭代器对象中

    • 通常由对象结构负责迭代,或是让Accept操作遍历该元素的各子构件并对每一个递归调用Accept。

  4. 单分派和双分派问题

    • C++只能实现accept的单分派:取决于请求的名和接收者的类型。

    • accept的双分派是:执行的操作取决于请求的种类和两个接收者的类型(Visitor和Node的类型)。在支持多分派的语言中,该方法的必需性就变小了

要素

  • 每个Node类中有一个 accept( Visitor* ), 这里的Node是整个对象结构中的类对象, 注意这里并不要求Node继承于同一父类

  • Visitor类中有(多个) visitConcreteNode'X'( ConcreteNode'X'* ); 名字最好取成操作的名称。

  • ConcreteVisitor实现对应于每个Node的操作实现。为操作(因为新这里实现的算法可能只是整个算法的片段)提供上下文存储其局部状态

  • 客户使用具体的Visitor对象和对象结构中的具体结点元素Node

  • 协作

    • 创建ConcreteVisitor对象,遍历对象结构,并用ConcreteVisitor访问每一个结点
    • 当一个元素被访问时,调用对应于它的类的Visitor操作。如果必要(需要改变结点状态),该结点将自身作为参数传递给该Visitor。

优点

  • 易于添加新的操作 —— 增加一个新的Visitor即可在一个对象上定义一个新的操作。这使得增加依赖于复杂对象结构的构件的操作变简单了。
  • ConcreteVisitor集中于单一职责,分离了无关操作。
  • 这里的结点对象不要求继承同一父类,只要在Visitor中设置了对应的接口即可遍历。

不足

  • 增加新的结点会很困难 —— 需要修改所有的访问者类家族。
  • Visitor在遍历所有结点时可能会累积状态(当状态作为参数提供给visitConcreteNode接口时)。
  • 访问者实现对结点功能的更新时,或visitConcreteNode接口的功能足够强,以至于可以履行结点职责时,迫使Visitor提供被访问结点的内部状态操作,从而破坏封装性

适用环境

适用于数据结构/对象结构稳定的系统,其将数据结构作用于数据结构上的操作分离开,使操作集合:

  • 对象结构中的类对象数量很少变化,但需要在此结构上定义新的操作;
  • 该对象结构被需要应用共享时,用Visitor来实现对不同应用的特化操作;
  • 一个对象结构中有许多类对象,且它们有不同的接口,想对这些对象实施一些依赖于某些具体类的操作(将相关操作封装为一个操作对象,从而和不相关的操作隔离)