访问者模式论文.doc_第1页
访问者模式论文.doc_第2页
访问者模式论文.doc_第3页
访问者模式论文.doc_第4页
访问者模式论文.doc_第5页
免费预览已结束,剩余9页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

程序设计模式结课论文2012 20013学年 第 2 学期论文题目 访问者模式 学生姓名 学 号 专业班级 指导教师 2013 年 7 月 1 日目录一、 访问者模式概述3二、 访问者模式的定义4三、 访问者模式的结构和使用51. 访问者模式涉及的角色52. 访问者模式的UML类图53. 模式的使用6四 访问者模式的优点8五 适合使用访问者模式的情景8静态分派9动态分派10七综合应用举例111、 访问者模式概述编写类的时候,可能在该类中编写了若干个实例方法,该类的对象通过调用这些实例方法操作其成员变量表明所产生的行为。在某些设计中,可能需要定义作用于类的成员变量的新操作,而且这个新操作不应当由该类中的某个实例方法来承担。比如,有一个Ammeter类,在Ammeter类中,electricAmmeter成员变量的值表示用电量,showElectricAmmeter()方法返回electricAmmeter变量的值。现在的问题是:希望根据用电量来计算电费,即根据Ammeter类中增加计算电费的方法。在实际生活中,应当由物业部门的“计表员”观察电表的用电量,然后按着有关收费标准计算出电费。访问者模式建议让一个称作访问者的对象访问Ammeter对象,以便定义作用于Ammeter对象上的操作。在访问者模式中,“计表员”是AmmeterVisitor类的实例,称作Ammeter类实例的访问者,AmmeterVisitor类中有一个计算电费的方法: void visit(Ammeter ammeter);该参数的方法是Ammeter类的实例,因此只要将Ammeter类的实例传递给该方法的参数,AmmeterVisitor类的实例就可如下计算电费:Double visit(Ammeter ammeter)Charge = ammeter.showElectricAmount() * 3.89;return charge;按着访问者模式,应当在AMmeter类中增加一个接受访问者的方法。Ammeter类中接受访问者的方法可如下定义:Void accept ( AmmeterVisitor v)V.visit(this);因此,一个Ammeter类的实例通过调用accept()方法,并向该方法传递一个访问者,即Ammetervisitor的实例,然后Ammeter类的实例再将自身传递给访问者就可以知道自己需要交纳多少电费了。Ammeter类有了接受访问者的accept(AmmeterVisitor visitor)方法后,可以不改变Ammeter类就可能定义作用于Ammeter对象的成员变量上的新操作,比如,可以让accept的参数是AmmeterVisitor类的一个实例,该实例不仅根据Ammeter对象的成员变量计算出正常的电费,而且也能计算超电量所应缴纳的额外的费用,当一个集合中邮若干个对象时,习惯上将这些对象称作集合中的元素,访问者模式可以在不改变集合中各个元素诶的前提下定义作用于这些元素上的新操作。比如,把一栋楼比作一个集合,那么电表就是集合中的元素,计表员“访问”楼房中的电表,并根据电表显示的数字计算电费。如下图: 楼房 计表员 2、 访问者模式的定义 设计模式一书对于访问者模式给出的定义为:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。从 定义可以看出结构对象是使用访问者模式必须条件,而且这个结构对象必须存在遍历自身各个对象的方法。这便类似于java中的collection概念了。 以下是访问者模式的组成结构: 1) 访问者角色(Visitor):为该对象结构中具体元素角色声明一个访问操作接口。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色。这样访问者就可以通过该元素角色的特定接口直接访问它。 2) 具体访问者角色(Concrete Visitor):实现每个由访问者角色(Visitor)声明的操作。 3) 元素角色(Element):定义一个Accept操作,它以一个访问者为参数。 4) 具体元素角色(Concrete Element):实现由元素角色提供的Accept操作。 5) 对象结构角色(Object Structure):这是使用访问者模式必备的角色。它要具备以下特征:能枚举它的元素;可以提供一个高层的接口以允许该访问者访问它的元素;可以是一个复合(组合模式)或是一个集合,如一个列表或一个无序集合。3、 访问者模式的结构和使用1. 访问者模式涉及的角色 (1)抽象访问者角色(Visitor):声明了一个或者多个访问操作,形成所有的集体元素角色必须实现的接口 (2)具体访问者角色(ConcreteVisitor):实现抽象访问者所声明的接口,也就是抽象访问者所声明的各个访问操作。 (3)抽象节点角色(Node):声明一个接受操作,接受一个访问者对象作为一个参量。 (4)具体节点角色(Node):实现了抽象元素所规定的接受操作。 (5)结构对象角色(ObjectStructure):可以遍历结构中所有的元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素;如果需要,可以设计成一个复合对象或一个集合,如(List)或(Set)。2. 访问者模式的UML类图访问者的类图模式如下所示:计表员是访问者模式中的具体访问者角色,楼房是对象结构角色,电表是具体元素角色。 3. 模式的使用下面是典型的访问者模式中使用的类:典型的具体访问者类代码如下所示:public class ConcreteVisitor extends Visitorpublic void visit(ConcreteElementA elementA)/元素ConcreteElementA操作代码public void visit(ConcreteElementB elementB)/元素ConcreteElementB操作代码 典型的抽象元素类代码如下所示:public interface Elementpublic void accept(Visitor visitor); 典型的具体元素类代码如下所示:public class ConcreteElementA implements Elementpublic void accept(Visitor visitor)visitor.visit(this);public void operationA()/业务方法 典型的对象结构类代码如下所示:public class ObjectStructureprivate ArrayList list=new ArrayList();public void accept(Visitor visitor)Iterator i=list.iterator();while(i.hasNext()(Element)i.next().accept(visitor);public void addElement(Element element)list.add(element);public void removeElement(Element element)list.remove(element); 4 访问者模式的优点l 可以在不改变一个集合中元素的情况下,增加新的施加于该元素上的新操作。l 可以将集合中各个元素的某些操作集中到访问者中,不仅便于集合的维护,也有利于集合中元素的使用。5 适合使用访问者模式的情景1)一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。 2) 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。 3) 当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。 4) 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。 六. 访问者模式在java中的应用变量被声明时的类型叫做变量的静态类型(Static Type),有些人又把静态类型叫做明显类型(Apparent Type);而变量所引用的对象的真实类型又叫做变量的实际类型(Actual Type)。比如:List list = null;list = new ArrayList();声明了一个变量list,它的静态类型(也叫明显类型)是List,而它的实际类型是ArrayList。 根据对象的类型而对方法进行的选择,就是分派(Dispatch),分派(Dispatch)又分为两种,即静态分派和动态分派。静态分派(Static Dispatch)发生在编译时期,分派根据静态类型信息发生。静态分派对于我们来说并不陌生,方法重载就是静态分派。动态分派(Dynamic Dispatch)发生在运行时期,动态分派动态地置换掉某个方法。静态分派Java通过方法重载支持静态分派。用墨子骑马的故事作为例子,墨子可以骑白马或者黑马。在这个系统中,墨子由Mozi类代表public class Mozi public void ride(Horse h) System.out.println(骑马); public void ride(WhiteHorse wh) System.out.println(骑白马); public void ride(BlackHorse bh) System.out.println(骑黑马); public static void main(String args) Horse wh = new WhiteHorse(); Horse bh = new BlackHorse(); Mozi mozi = new Mozi(); mozi.ride(wh); mozi.ride(bh); 显然,Mozi类的ride()方法是由三个方法重载而成的。这三个方法分别接受马(Horse)、白马(WhiteHorse)、黑马(BlackHorse)等类型的参数。那么在运行时,程序会打印出什么结果呢?结果是程序会打印出相同的两行“骑马”。换言之,墨子发现他所骑的都是马。为什么呢?两次对ride()方法的调用传入的是不同的参数,也就是wh和bh。它们虽然具有不同的真实类型,但是它们的静态类型都是一样的,均是Horse类型。重载方法的分派是根据静态类型进行的,这个分派过程在编译时期就完成了。动态分派Java通过方法的重写支持动态分派。用马吃草的故事作为例子,代码如下所示:public class Horse public void eat() System.out.println(马吃草); public class BlackHorse extends Horse public void eat() System.out.println(黑马吃草); public class Client public static void main(String args) Horse h = new BlackHorse(); h.eat();变量h的静态类型是Horse,而真实类型是BlackHorse。如果上面最后一行的eat()方法调用的是BlackHorse类的eat()方法,那么上面打印的就是“黑马吃草”;相反,如果上面的eat()方法调用的是Horse类的eat()方法,那么打印的就是“马吃草”。所以,问题的核心就是Java编译器在编译时期并不总是知道哪些代码会被执行,因为编译器仅仅知道对象的静态类型,而不知道对象的真实类型;而方法的调用则是根据对象的真实类型,而不是静态类型。这样一来,上面最后一行的eat()方法调用的是BlackHorse类的eat()方法,打印的是“黑马吃草”。七综合应用举例在图中有两个对象,左边的叫做West,右边的叫做East。现在West对象首先调用East对象的goEast()方法,并将它自己传入。在East对象被调用时,立即根据传入的参数知道了调用者是谁,于是反过来调用“调用者”对象的goWest()方法。通过两次调用将程序控制权轮番交给两个对象。源代码如下:West类:public abstract class West public abstract void goWest1(SubEast1 east); public abstract void goWest2(SubEast2 east);SubWest1类:public class SubWest1 extends West public void goWest1(SubEast1 east) System.out.println(SubWest1 + + east.myName1(); public void goWest2(SubEast2 east) System.out.println(SubWest1 + + east.myName2(); public class SubWest2 extends West public void goWest1(SubEast1 east) System.out.println(SubWest2 + + east.myName1(); public void goWest2(SubEast2 east) System.out.println(SubWest2 + + east.myName2(); East类public abstract class East public abstract void goEast(West west);SubEast1类public class SubEast1 extends East public void goEast(West west) west.goWest1(this); public String myName1() return SubEast1; SubEast2类public class SubEast2 extends East public void goEast(West west) west.goWest2(this); public String myName2() return SubEast2; 客户端类public class Client public static void main(String args) /组合1 East east = new SubEast1(); West west = new SubWest1(); east.goEast(west); /组合2 east = new SubEast1(); west = new SubWest2(); east.goEast(west); 运行结果如下:SubWest1 + SubEast1SubWest2 + SubEast1系统运行时,会首先创建SubWes

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论