- 浏览: 62688 次
- 性别:
- 来自: 北京
文章分类
最新评论
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根据接收的参数来分发打印操作。
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]
表示一个作用于某对象结构中的各元素的操作。将对象结构中的算法从对象结构中分离出来,它使得你可以再不改变各元素的类的前提下定义作用于这些元素的新操作。
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
发表评论
-
STRATEGY(策略)——对象行为型模式
2013-06-17 16:19 6841、意图 定义一系列的算法,把它们一个个封装起来,并且 ... -
STATE(状态)—— 对象行为型模式
2013-06-14 16:49 7831、意图 允许一个 ... -
OBSERVER(观察者)——对象行为型模式
2013-06-13 15:36 7041、意图 定义对象 ... -
MEDIATOR(中介者)——对象行为型模式
2013-06-09 16:17 6821、意图 用一个中 ... -
CHAIN OF RESPONSIBILITY(职责链) —— 对象行为型模式
2013-06-06 16:32 6481、意图 使多个对象都有机会处理请求,从而避免请求的发 ... -
PROXY(代理) —— 对象结构型模式
2013-06-05 11:24 7541、意图 为其他对 ... -
DECORATOR(装饰) -—— 对象机构型模式
2013-06-03 11:43 6981、意图 动态地给一个对象添加一些额外的职责。就增加功 ... -
COMPOSITE(组合) ---- 对象结构型模式
2013-05-31 15:12 7721、意图 将对象组 ... -
FACTORY METHOD(工厂方法) ---- 对象创建型模式
2013-05-28 11:43 7231、意图 定义一个用于创建对象的接口,让子类决定实例化 ... -
设计模式的三大分类解析
2013-05-24 14:48 848设计模式在功能上 ... -
TEMPLATE METHOD(模板方法)----- 类行为型模式
2013-04-07 11:27 7011、意图 定义一个 ... -
ADAPTER(适配器) --- 类对象结构型模式
2013-03-28 11:33 5951、意图 将一个类的接口转化成客户希望的另外一个接口。 ... -
BRIGE(桥接) ------ 对象结构型模式
2013-03-27 11:20 7401、意图 将抽象部 ... -
COMMAND(命令) ---- 对象行为型模式
2013-03-06 11:20 7251、意图 将一个请求封装为一个对象,从而使你可用不同的 ... -
Builder(生成器)---- 对象创建型模式
2013-02-25 15:54 6331、意图 将一个复杂对象的创建和它的表示分离,使得同样的 ... -
ABSTRACT FACTORY(抽象工厂)------ 对象创建型模式
2013-02-22 11:21 6831、意图 提供一个创建一系列相关或相互依赖对象的接口,而 ... -
Flyweight pattern(享元模式)
2013-02-20 14:45 7701. 概述 面向对象技术可以很好地解决系统一些灵活性或可扩展 ... -
Facade模式 --- 对象结构型模式
2013-02-19 17:03 7021、意图 为子系统中 ... -
23个设计模式的名字和意图
2013-02-19 15:41 7221、Abstract Factory:提供 ... -
从MVC理解设计模式
2013-02-19 14:40 776本文将透过MVC来帮助我们理解“模式”这一术语的含义。 ...
相关推荐
**Visitor模式**提供了一种解决方案,即通过将操作封装在访问者对象中,可以独立于元素类层次结构地为系统添加新的行为。这样不仅避免了对现有类结构的修改,还能保持代码的清晰性和可扩展性。 #### 结构与组成部分...
C#面向对象设计模式 (行为型模式) Visitor 访问者模式 视频讲座下载
访问者模式(Visitor)是一种行为设计模式,它允许在不修改对象结构的前提下向对象结构中的元素添加新的操作。这种模式将算法与数据结构分离,使得算法可以独立于数据结构进行变化,增强了系统的可扩展性。 在C++中...
2. 对每个元素,调用`accept`方法,传入访问者对象。 3. 具体元素的`accept`方法调用访问者的相应访问方法,执行特定的操作。 4. 访问者根据需要访问不同类型的元素,执行不同的操作。 **访问者模式的优势** 1. **...
访问者模式(Visitor)是一种行为设计模式,它允许在不修改对象结构的前提下向对象结构中的元素添加新的操作。这种模式的核心思想是分离了算法和对象结构,使得算法可以在不改变对象结构的情况下独立变化。 访问者...
### 设计模式-访问者(Visitor)模式详解和应用 #### 一、访问者模式简介 访问者模式(Visitor Pattern)是一种行为型设计模式,它允许我们向一组已存在的类添加新的行为,而无需修改这些类。这种模式的核心思想是在...
访问者模式是一种行为设计模式,它使你能在不修改对象结构的前提下向对象添加新的操作。这种模式常用于处理具有复杂逻辑的对象结构,特别是当你需要对这些对象进行多态操作时。访问者模式将算法与数据结构分离,使得...
C#面向对象设计模式纵横谈(24):(行为型模式) Visitor 访问者模式.rar 李建忠主讲 上海祝成信息科技有限公司担任软件架构师,并讲授C#/.NET软件开发课程
**访问者模式(Visitor Pattern)**是一种行为设计模式,它提供了一种在不修改对象结构的情况下增加新操作的方法。这种模式的主要思想是将数据结构与算法分离,使得算法可以在不改变对象结构的情况下独立变化。 在...
这个方法接收一个访问者对象,并调用其访问该元素的方法。 4. **具体元素(Concrete Element)** 具体元素类实现元素接口,并提供实际的数据和行为。它们持有对其他元素或数据的引用,形成了对象结构的一部分。 5...
本项目基于“visitor模式”和“访问者模式”,实现了用于计算表达式的求值引擎,这涉及到一种将数学表达式转化为数据结构(表达式树)的方法,然后通过遍历该树来执行计算。下面我们将详细探讨这些概念。 1. **...
3. **抽象元素(Element)**:定义一个接受操作的接口,用于接受访问者对象的访问。 4. **具体元素(Concrete Element)**:实现了抽象元素中定义的接受访问者的方法,并向访问者暴露自己的内部结构。 5. **对象结构...
"设计模式系列之visitor"是一个关于软件设计模式的讨论,特别是关注于“访问者”(Visitor)模式。这个模式是GOF(Gamma, Helm, Johnson, Vlissides)在他们的经典著作《设计模式:可复用面向对象软件的基础》中提出...
访问者模式是一种行为设计模式,它允许在不修改对象结构的情况下向对象添加新的操作。这种模式的核心思想是将数据结构与算法分离,使得算法可以在不改变对象结构的前提下增加对对象的操作。 在软件开发中,有时我们...
访问者模式是软件设计模式中行为型模式的一种,它是一种能够为对象结构中的对象添加新的操作,而无需改变其类的行为模式。在访问者模式中,将数据操作和数据结构分离,使得增加新的操作变得容易,同时保持了操作的...
访问者模式(Visitor Pattern)是一种行为型设计模式,旨在不改变被访问对象结构的情况下,定义对其元素的新操作。访问者模式通过将操作封装在独立的类中,使得新的操作可以灵活地添加,而无需修改对象的结构。访问...
2. **具体元素(Concrete Element)**:实现了元素接口,通常会持有访问者对象并调用其访问方法。 3. **访问者接口(Visitor)**:定义了访问每个元素的接口,即定义了对每种具体元素进行操作的`visit()`方法。 4....
访问者模式是一种软件设计模式,它在对象结构中定义了一个访问者的接口,使得该访问者可以访问该结构中的每一个元素,同时不影响这些元素自身的行为。这种模式的主要目的是将数据操作和业务逻辑分离,使得数据结构...
3. 抽象元素(Element):声明一个接受操作,接受一个访问者对象作为参数。这个操作通常会调用访问者中的相应方法,以实现对元素的访问。 4. 具体元素(Concrete Element):实现抽象元素接口,提供具体的元素信息...