`

VISITOR(访问者) ---- 对象行为型模式

 
阅读更多
1、意图

       表示一个作用于某对象结构中的各元素的操作。将对象结构中的算法从对象结构中分离出来,它使得你可以再不改变各元素的类的前提下定义作用于这些元素的新操作。

2、动机

     考虑一个编译器,它将源程序表示为一个抽象语法树。该编译器需在抽象语法树上实施某些操作以进行“静态语义”分析,例如检查是否所有变量都已经被定义了。它也需要生成代码。因此它可能要定义许多操作以进行类型检查、代码优化、流程分析、检查变量是否在使用前被赋初值,等等。此外,还可使用抽象语法树进行优美格式打印、程序重构、code instrumentation以及对程序进行多种度量。

     这些操作大多要求对不同的节点进行不同的处理。例如对代表复制语句的节点的处理就不同于代表变量或算数表达式的节点的处理。因此有用于赋值语句的类,有用于变量访问的类,还有用于算数表达式的类,等等。节点类的集合当然依赖于被编译的语言,但对于一个给定的语言其变化不大。



      上图的框架显示了Node类层次的一部分。这里的问题是,将所有的操作分散到各种节点类中会导致整个系统难以理解、难以维护和修改。将类型检查代码和优美格式打印代码或流程分析代码放到一起,将产生混乱。此外,增加新的操作通常需要重新编译所有的类。如果可以单独地增加新的操作,并且使这些节点类独立于作用于其上的操作,将会更好一些。

     要实现上述两个目标,我们可以讲每一个类中的相关操作包装在一个独立的对象(称为一个Visitor)中,并在遍历抽象语法树时将此对象传递给当前访问的元素。当一个元素“接受”该访问者时,该元素向访问者发送一个包含自身类信息的请求。该请求同时也将该元素本身作为一个参数。然后访问者将为该元素执行该操作 ---- 这一操作以前是在该元素的类中的。

     例如,一个不使用访问者的编译器可能会通过在它的语法树上调用TpyeCheck操作对一个过程进行类型检查。每一个节点将对调用它的成员的TypeCheck以实现自身的TypeCheck(参看前面的类框图)。如果该编译器使用访问者对一个过程进行类型检查,那么它将会创建一个TypeCheckVisitor对象,并以这个对象为一个参数在抽象语法树上调用Accept操作。每一个节点在实现Accept时将会回调访问者:一个赋值节点调用访问者的Assignment操作,而一个引用将调用VisitVariableReference。以前类AssignmentNode的TypeCheck操作现在成为了TypeCheckVisitor的VisitAssignment操作。

     为使访问者不仅仅只做类型检查,我们需要所有抽象语法树的访问者都有一个抽象的父类NodeVisitor。NodeVisitor必须为每一个结点定义一个操作。一个需要计算程序度量的应用将应以NodeVisitor的新的子类,并且将不再需要在结点类中增加与特定应用相关的代码。Visitor模式将每一个编译步骤的操作封装在一个与该步骤相关的Visitor中(参见下图)。







3、实用性

      在以下情况下使用Visitor模式:

     1) 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其他具体类的操作

     2)  需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。

   3)  定义对象结构的类很少改变,当经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么还是在这些类中定义这些操作比较好。

4、结构




6、参与者

Visitor(访问者,如Node Visitor)

---- 为该对象结构中的ConcreteElement的每一个类声明一个Visit操作。该操作的名字和特征标识了发送Visit请求给访问者的那个类。这使得访问者可以确定正被访问元素的具体的类。这样访问者就可以通过该元素的特定接口直接访问它。

ConcreteVisitor(具体访问者,如TypeCheckingVisitor)

---- 实现每个由Visitor声明的操作。每个操作实现本算法的一部分,而该算法片段乃是对应于结构中对象的类。ConcreteVisitor为该算法提供了上下文并存储它的局部状态。这一状态常常在遍历该结构的过程中累计结果。

Element(元素,如Node)

---- 定义一个Accept操作,它以一个访问者问参数。

ConcreteElement(具体元素,如AssignmentNode,VariableRefNode)

---- 实现Accept操作,该操作以一个访问者为参数。

ObjectStructure(对象结构,如Program)

---- 能枚举它的元素。

---- 可以提供一个高层的接口以允许该访问者访问它的元素。

---- 可以使一个复合或是一个集合,如一个列表或一个无序集合。

7、代码实例

       下例将展示如何打印一个结点数的内容,由一个单独的类来执行打印操作而不是在每个结点子类中创建一个打印方法,因为不同的子类需要不同的展示方式,CarElementDoVisitor根据接收的参数来分发打印操作。




interface CarElementVisitor {
    void visit(Wheel wheel);
    void visit(Engine engine);
    void visit(Body body);
    void visit(Car car);
}
 
interface CarElement {
    void accept(CarElementVisitor visitor); // CarElements have to provide accept().
}
 
class Wheel implements CarElement {
    private String name;
 
    public Wheel(String name) {
        this.name = name;
    }
 
    public String getName() {
        return this.name;
    }
 
    public void accept(CarElementVisitor visitor) {
        /*
         * accept(CarElementVisitor) in Wheel implements
         * accept(CarElementVisitor) in CarElement, so the call
         * to accept is bound at run time. This can be considered
         * the first dispatch. However, the decision to call
         * visit(Wheel) (as opposed to visit(Engine) etc.) can be
         * made during compile time since 'this' is known at compile
         * time to be a Wheel. Moreover, each implementation of
         * CarElementVisitor implements the visit(Wheel), which is
         * another decision that is made at run time. This can be
         * considered the second dispatch.
         */ 
        visitor.visit(this);
    }
}
 
class Engine implements CarElement {
    public void accept(CarElementVisitor visitor) {
        visitor.visit(this);
    }
}
 
class Body implements CarElement {
    public void accept(CarElementVisitor visitor) {
        visitor.visit(this);
    }
}
 
class Car implements CarElement {
    CarElement[] elements;
 
    public Car() {
        //create new Array of elements
        this.elements = new CarElement[] { new Wheel("front left"), 
            new Wheel("front right"), new Wheel("back left") , 
            new Wheel("back right"), new Body(), new Engine() };
    }
 
    public void accept(CarElementVisitor visitor) {     
        for(CarElement elem : elements) {
            elem.accept(visitor);
        }
        visitor.visit(this);    
    }
}
 
class CarElementPrintVisitor implements CarElementVisitor {
    public void visit(Wheel wheel) {      
        System.out.println("Visiting " + wheel.getName() + " wheel");
    }
 
    public void visit(Engine engine) {
        System.out.println("Visiting engine");
    }
 
    public void visit(Body body) {
        System.out.println("Visiting body");
    }
 
    public void visit(Car car) {      
        System.out.println("Visiting car");
    }
}
 
class CarElementDoVisitor implements CarElementVisitor {
    public void visit(Wheel wheel) {
        System.out.println("Kicking my " + wheel.getName() + " wheel");
    }
 
    public void visit(Engine engine) {
        System.out.println("Starting my engine");
    }
 
    public void visit(Body body) {
        System.out.println("Moving my body");
    }
 
    public void visit(Car car) {
        System.out.println("Starting my car");
    }
}
 
public class VisitorDemo {
    static public void main(String[] args) {
        CarElement car = new Car();
        car.accept(new CarElementPrintVisitor());
        car.accept(new CarElementDoVisitor());
    }
}


Note: A more flexible approach to this pattern is to create a
wrapper class implementing the interface defining the accept method. The
wrapper contains a reference pointing to the CarElement which could be
initialized through the constructor. This approach avoids having to
implement an interface on each element. [see article Java Tip 98 article
below]


Output
Visiting front left wheel
Visiting front right wheel
Visiting back left wheel
Visiting back right wheel
Visiting body
Visiting engine
Visiting car
Kicking my front left wheel
Kicking my front right wheel
Kicking my back left wheel
Kicking my back right wheel
Moving my body
Starting my engine
Starting my car

  • 大小: 35.2 KB
  • 大小: 97.6 KB
  • 大小: 111.4 KB
  • 大小: 48.6 KB
分享到:
评论

相关推荐

    (行为型模式) Visitor 访问者模式

    C#面向对象设计模式 (行为型模式) Visitor 访问者模式 视频讲座下载

    C#面向对象设计模式纵横谈(24):(行为型模式) Visitor 访问者模式

    C#面向对象设计模式纵横谈(24):(行为型模式) Visitor 访问者模式

    C#面向对象设计模式纵横谈(24):(行为型模式) Visitor 访问者模式.rar

    C#面向对象设计模式纵横谈(24):(行为型模式) Visitor 访问者模式.rar 李建忠主讲 上海祝成信息科技有限公司担任软件架构师,并讲授C#/.NET软件开发课程

    设计模式-访问者模式(Visitor)

    Visitor(访问者模式)属于行为型模式。意图:表示一个作用于某对象结构中的各元素的操作。

    设计模式--可复用面向对象软件的基础

    5.11 VISITOR(访问者)——对象行为型模式 5.12 行为模式的讨论 第六章 结论 6.1 设计模式将带来什么 6.2 一套通用的设计词汇 6.3 书写文档和学习的辅助手段 6.4 现有方法的一种补充 6.5 重构的目标 6.6 本书简史 ...

    访问者模式(Visitor)原理图

    访问者模式的基本思想是,针对系统中拥有固定类型数的对象结构(元素),在其内提供一个accept()方法来接受访问者对象的访问。不同的访问者对同一个元素的访问内容是不同,使得相同的元素集合可以产生不同的数据结果...

    设计模式可复用面向对象软件的基础.zip

    5.11 VISITOR(访问者)—对象行为型 模式 218 5.12 行为模式的讨论 228 5.12 1 封装变化 228 5.12.2 对象作为参数 228 5.12.3 通信应该被封装还是被分布 229 5.12.4 对发送者和接收者解耦 229 5.12.5 总结 231 第6...

    C#面向对象设计模式纵横谈(视频与源码)

    讲 师:李建忠 上海祝成信息科技有限公司 高级培训讲师 MSDN特邀讲师 C#面向对象设计模式纵横谈... C#面向对象设计模式纵横谈(24):(行为型模式) Visitor 访问者模式 C#面向对象设计模式纵横谈(25):设计模式总结

    设计模式--C++

    5.11 VISITOR(访问者)—对象行为型模式 218 5.12 行为模式的讨论 228 5.12 1 封装变化 228 5.12.2 对象作为参数 228 5.12.3 通信应该被封装还是被分布 229 5.12.4 对发送者和接收者解耦 229 5.12.5 总结 231 第 6 ...

    设计模式:可复用面向对象软件的基础--详细书签版

    5.11 visitor(访问者)—对象行为型 模式 218 5.12 行为模式的讨论 228 5.12 1 封装变化 228 5.12.2 对象作为参数 228 5.12.3 通信应该被封装还是被分布 229 5.12.4 对发送者和接收者解耦 229 5.12.5 总结 ...

    《设计模式可复用面向对象软件的基础》

    2145.11 VISITOR(访问者)—对象行为型模式 2185.12 行为模式的讨论 2285.12 1 封装变化 2285.12.2 对象作为参数 2285.12.3 通信应该被封装还是被分布 2295.12.4 对发送者和接收者解耦 2295.12.5 总结 231第6章 ...

    软件设计师必读的书-设计模式

    5.11 VISITOR(访问者)—对象行为型 模式 218 5.12 行为模式的讨论 228 5.12 1 封装变化 228 5.12.2 对象作为参数 228 5.12.3 通信应该被封装还是被分布 229 5.12.4 对发送者和接收者解耦 229 5.12.5 总结 231 第6...

    design-pattern-java.pdf

    算法的封装与切换——策略模式(四) 模板方法模式-Template Method Pattern 模板方法模式深度解析(一) 模板方法模式深度解析(二) 模板方法模式深度解析(三) 访问者模式-Visitor Pattern 操作复杂对象结构——...

    设计模式可复用面向对象软件的基础.

    5.11 VISITOR(访问者)—对象行为型 模式 218 5.12 行为模式的讨论 228 5.12 1 封装变化 228 5.12.2 对象作为参数 228 5.12.3 通信应该被封装还是被分布 229 5.12.4 对发送者和接收者解耦 229 5.12.5 总结 231 第6...

    设计模式:可复用面向对象软件的基础

    5.11 VISITOR(访问者)—对象行为型 模式 218 5.12 行为模式的讨论 228 5.12 1 封装变化 228 5.12.2 对象作为参数 228 5.12.3 通信应该被封装还是被分布 229 5.12.4 对发送者和接收者解耦 229 5.12.5 总结 231 第6...

    《设计模式》中文版(23个设计模式的介绍与运用)

    5.11 VISITOR(访问者)—对象行为型 模式 218 5.12 行为模式的讨论 228 5.12 1 封装变化 228 5.12.2 对象作为参数 228 5.12.3 通信应该被封装还是被分布 229 5.12.4 对发送者和接收者解耦 229 5.12.5 总结 231 第6...

    GOLF设计模式(C++语言版)

    5.11 VISITOR(访问者)—对象行为型 模式 218 5.12 行为模式的讨论 228 5.12 1 封装变化 228 5.12.2 对象作为参数 228 5.12.3 通信应该被封装还是被分布 229 5.12.4 对发送者和接收者解耦 229 5.12.5 总结 ...

    设计模式(.PDF)

    5.11 VISITOR(访问者)—对象行为型 模式 218 5.12 行为模式的讨论 228 5.12 1 封装变化 228 5.12.2 对象作为参数 228 5.12.3 通信应该被封装还是被分布 229 5.12.4 对发送者和接收者解耦 229 5.12.5 总结 231 第6...

Global site tag (gtag.js) - Google Analytics