`
hdy007
  • 浏览: 31221 次
最近访客 更多访客>>
文章分类
社区版块
存档分类

深入visitor模式

阅读更多
一,访问者模式的角色:
抽象访问者:声明一个或者多个访问操作,形成所有的具体元素都要实现的接口
具体访问者:实现抽象访问者所声明的接口
抽象节点:声明一个接受操作,接受一个访问者对象作为参量
具体节点:实现了抽象元素所规定的接受操作
结构对象:遍历结构中的所有元素,类似List Set等
二,在什么情况下应当使用访问者模式
访问者模式应该用在被访问类结构比较稳定的时候,换言之系统很少出现增加新节点的
情况。因为访问者模式对开-闭原则的支持并不好,访问者模式允许在节点中加入方法,
是倾斜的开闭原则,类似抽象工厂。
三,访问者模式的缺点:
1,增加节点困难
2,破坏了封装
因为访问者模式的缺点和复杂性,很多设计师反对使用访问者模式。个人感觉应该在了解的
情况下考虑衡量选择.

静态分派,动态分派,多分派,单分派 -------------- visitor模式准备 
一,静态分派:
1,定义:发生在编译时期,分派根据静态类型信息发生,重载就是静态分派
2,什么是静态类型:变量被声明时的类型是静态类型
      什么是动态类型:变量所引用的对象的真实类型
3,有两个类,BlackCat ,WhiteCat都继承自Cat
如下调用
class Cat{}
class WhiteCat extends Cat{}
class BlackCat extends Cat{}
public class Person {
    public void feed(Cat cat){
        System.out.println("feed cat");
    }
    public void feed(WhiteCat cat){
        System.out.println("feed WhiteCat");
    }
    public void feed(BlackCat cat){
        System.out.println("feed BlackCat");
    }
    public static void main(String[] args) {
        Cat wc = new WhiteCat();
        Cat bc = new BlackCat();
        Person p = new Person();
        p.feed(wc);
        p.feed(bc);
    }

}
运行结果是:
feed cat
feed cat
这样的结果是因为重载是静态分派,在编译器执行的,取决于变量的声明类型,因为wc ,bc都是Cat所以调用的都是feed(Cat cat)的函数.
二,动态分派
1,定义:发生在运行期,动态分派,动态的置换掉某个方法。
还是上边类似的例子:
class Cat{
    public void eat(){
        System.out.println("cat eat");
    }
}
public class BlackCat extends Cat{
    public void eat(){
        System.out.println("black cat eat");
    }
    public static void main(String[] args){
        Cat cat = new BlackCat();
        cat.eat();
    }
}这个时候的结果是:
black cat eat
这样的结果是因为在执行期发生了向下转型,就是动态分派了。

三,单分派:
1,定义:根据一个宗量的类型进行方法的选择
四,多分派:
1,定义:根据多于一个宗量的类型对方法的选择
2,说明:多分派其实是一系列的单分派组成的,区别的地方就是这些但分派不能分割。
3,C++ ,Java都是动态单分派,静态多分派语言
多分派的语言有:CLOS  Cecil


访问差异类型的集合类--visitor模式入门
访问差异类型的集合类--visitor模式入门
本文对应代码下载这里
一,问题提出
访问同一类型的集合类是我们最常见的事情了,我们工作中这样的代码太常见了。


1  Iterator ie  =  list.iterator();
2   while (ie.hasNext())  {
3     Person p  =  (Person)ie.next();
4     p.doWork();
5 } 

这种访问的特点是集合类中的对象是同一类对象Person,他们拥有功能的方法run,我们调用的恰好是这个共同的方法。
在大部份的情况下,这个是可以的,但在一些复杂的情况,如被访问者的继承结构复杂,被访问者的并不是同一类对象,
也就是说不是继承自同一个根类。方法名也并不相同。例如Java GUI中的事件就是一个例子。
例如这样的问题,有如下类和方法:
类:PA ,方法:runPA();
类:PB ,方法:runPB();
类:PC ,方法:runPC();
类:PD ,方法:runPD();
类:PE ,方法:runPE();
有一个集合类List
List list = new ArrayList();
list.add(new PA());
list.add(new PB());
list.add(new PC());
list.add(new PD());
list.add(new PE());
....
二:解决
要求能访问到每个类的对应的方法。我们第一反应应该是这样的。


 1  Iterator ie  =  list.iterator();
 2   while (ie.hasNext())  {
 3     Object obj  =  ie.next();
 4       if  (obj  instanceof  PA)  {
 5         ((PA)obj).runPA();
 6      } else   if (obj  instanceof  PB)  {
 7         ((PB)obj).runPB();
 8      } else   if (obj  instanceof  PC)  {
 9         ((PC)obj).runPC();
10      } else   if (obj  instanceof  PD)  {
11         ((PD)obj).runPD();
12      } else   if (obj  instanceof  PE)  {
13         ((PE)obj).runPE();
14     } 
15 } 

三:新问题及分析解决
当数目变多的时候,维护if else是个费力气的事情:
仔细分析if,else做的工作,首先判断类型,然後根据类型执行相应的函数
如何才能解决这两个问题呢?首先想到的是java的多态,多态就是根据参数执行相应的内容,
能很容易的解决第二个问题,我们可以写这样一个类:


 1   public   class  visitor  {
 2       public   void  run(PA pa)  {
 3         pa.runPA();
 4     } 
 5       public   void  run(PB pb)  {
 6         pb.runPB();
 7     } 
 8       public   void  run(PC pc)  {
 9         pc.runPC();
10     } 
11       public   void  run(PD pd)  {
12         pd.runPD();
13     } 
14       public   void  run(PE pe)  {
15         pe.runPE();
16     } 
17 } 

这样只要调用run方法,传入对应的参数就能执行了。
还有一个问题就是判断类型。由于重载(overloading)是静态多分配(java语言本身是支持"静态多分配"的。
关于这个概念请看这里)所以造成重载只根据传入对象的定义类型,而不是实际的类型,所以必须在传入前就确定类型,
这可是个难的问题,因为在容器中对象全是Object,出来后要是判断是什么类型必须用
if (xx instanceof xxx)这种方法,如果用这种方法启不是又回到了原点,有没有什么更好的办法呢?

我们知到Java还有另外一个特点,覆写(overriding),而覆写是"动态单分配"的(关于这个概念见这里),
那如何利用这个来实现呢?看下边这个方法:
 我们让上边的一些类PA PB PC PD PE都实现一个接口P,加入一个方法,accept();


 1   public   void  accept(visitor v)  {
 2      // 把自己传入1 
 3      v.run( this );
 4 } 
 5 然後在visitor中加入一个方法
 6   public   void  run(P p)  {
 7      // 把自己传入2 
 8      p.accept( this );
 9 } 
10  // 这样你在遍历中可以这样写 
11  Visitor v  =   new  Visitor();
12 Iterator ie  =  list.iterator();
13   while (ie.hasNext())  {
14     P p  =  (P)ie.next();
15         p.accept(v);
16     } 
17 } 

首先执行的是"把自己传入2",在这里由于Java的特性,实际执行的是子类的accept(),也就是实际类的accept
然後是"把自己传入1",在这里再次把this传入,就明确类型,ok我们巧妙的利用overriding解决了这个问题
其实归纳一下第二部分,一个关键点是"自己认识自己",是不是很可笑。
其实在计算计技术领域的很多技术上看起来很高深的东西,其实就是现有社会中人的生活方式的一种映射
而且这种方式是简单的不能再简单的方式。上边的全部过程基本上是一个简单的visitor模式实现,visitor模式
已经是设计模式中比较复杂的模式了,但其实原理简单到你想笑。看看下边这个比喻也许你的理解会更深刻。

四:一个帮助理解的比喻:
题目:指挥工人工作
条件:你有10个全能工人,10样相同工作。
需求:做完工作
实现:大喊一声所有人去工作

条件变了,工人不是全能,但是工作相同,ok问题不大
条件再变,工作不是相同,但工人是全能,ok问题不大

以上三种情况在现实生活中是很少发生得,最多的情况是这样:
10个工人,每人会做一种工作,10样工作。你又一份名单Collection)写着谁做什么。但你不认识任何人
这个时候你怎么指挥呢,方案一:
你可以一个个的叫工人,然後问他们名字,认识他们,查名单,告诉他们做什么工作。
你可以直接叫出他们名字,告诉他们干什么,不需要知到他是谁。
看起来很简单。但如果你要指挥10万人呢 ?而且人员是流动的,每天的人不同,你每天拿到一张文档。
其实很简单,最常用的做法是,你把这份名单贴在墙上,然後大喊一声,所有人按照去看,按照自己的分配情况去做。
这里利用的关键点是"所有工人自己认识自己",你不能苛求每个工人会做所有工作,不能苛求所有工作相同,但你
能要求所有工人都认识自己。

再想想我们开始的程序,每个工人对应着PA PB PC PD PE....
所有的工人都使工人P
每个工人会做的东西不一样runPA runPB runPC
你有一份名单Visitor(重载)记录着谁做什么工作。

看完上边这些,你是不是会产生如下的问题:
问题:为什么不把这些方法的方法名做成一样的,那就可以解决了。
例如,我们每个PA ,PB ,PC都加入一个run 方法,然後run内部再调用自己对应的runPx()方法。
答案:有些时候从不同的角度考虑,或者因为实现的复杂度早成很难统一方法名。
例如上边指挥人工作的例子的例子,其实run方法就是大叫一声去工作,因为每个工人只会做一种工作,所以能行
但我们不能要求所有人只能会做一种事情,这个要求很愚蠢。所以如果每个工人会干两种或者多种工作呢,
也就是我PA 有runPA() walkPA()等等方法, PB有runPB() climbPB()等等。。。
这个时候按照名单做事才是最好的办法。 

分享到:
评论

相关推荐

    基于visitor模式和访问者模式的表达式树_求值引擎

    本项目基于“visitor模式”和“访问者模式”,实现了用于计算表达式的求值引擎,这涉及到一种将数学表达式转化为数据结构(表达式树)的方法,然后通过遍历该树来执行计算。下面我们将详细探讨这些概念。 1. **...

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

    为了更深入地了解**Visitor模式**及其在实际项目中的应用,可以参考以下书籍和资源: - **《设计模式:可复用面向对象软件的基础》**(GoF):经典的设计模式指南,详细介绍了23种设计模式,包括Visitor模式。 - **...

    设计模式系列之visitor

    压缩包中的"visitor"文件可能包含对该模式的详细代码示例、讲解文档或者案例分析,供学习者深入理解访问者模式的工作原理和实际应用。通过阅读这些材料,你可以更全面地掌握这一设计模式,以便在未来开发中灵活运用...

    深入浅出设计模式附书源码Java版源码

    3. **行为型模式**:这类模式关注对象之间的交互和职责分配,例如策略模式(Strategy)、模板方法模式(Template Method)、观察者模式(Observer)、迭代器模式(Iterator)、访问者模式(Visitor)、责任链模式...

    深入浅出设计模式.rar

    当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新,模板方法模式(Template Method)在一个抽象类中定义了一个操作中的算法骨架,而将一些步骤延迟到子类中,访问者模式(Visitor)表示一个...

    深入浅出设计模式 C++ 源码 + 设计模式 C++

    3. 行为型模式:如策略(Strategy)、模板方法(Template Method)、观察者(Observer)、迭代器(Iterator)、访问者(Visitor)、命令(Command)、责任链(Chain of Responsibility)、备忘录(Memento)、状态...

    深入浅出设计模式 (希望对设计模式有所了解和深入研究的都适用)

    访问者模式(Visitor)在不修改对象结构的前提下,增加新的操作。 通过阅读《深入浅出设计模式》这本书,读者将能更好地理解这些模式的工作原理、应用场景以及如何在实际项目中应用它们。结合《HEAD FIRST设计模式...

    深入浅出的设计模式 ptf

    如责任链模式(Chain of Responsibility)、命令模式(Command)、解释器模式(Interpreter)、迭代器模式(Iterator)、中介者模式(Mediator)、...Strategy)、模板方法模式(Template Method)和访问者模式(Visitor)...

    深入浅出设计模式样章

    这类模式关注对象之间的交互和职责分配,例如策略模式(Strategy)、模板方法模式(Template Method)、观察者模式(Observer)、命令模式(Command)、迭代器模式(Iterator)、访问者模式(Visitor)、备忘录模式...

    24种设计模式以及混合设计模式

    如策略模式(Strategy)、模板方法模式(Template Method)、观察者模式(Observer)、命令模式(Command)、迭代器模式(Iterator)、访问者模式(Visitor)、备忘录模式(Memento)、状态模式(State)、职责链...

    HeadFirst设计模式(深入浅出设计模式)-高清

    20. **访问者模式(Visitor)**:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。 以上是《HeadFirst设计模式》一书中涵盖的主要设计模式,每种模式...

    深入浅出设计模式(中文版电子版)

    5.11VisitorPattern(访问者模式) 280 5.11.1定义 280 5.11.2现实例子——收银员收银计费 282 5.11.3C#实例——人事评估 283 5.11.4Java实例——维修工程师检查车辆 287 5.11.5优势和缺陷 291 5.11.6应用情??...

    研磨设计模式(完整带书签).part2.pdf

    本电子书一共两个压缩文档,本文件为part2. 《研磨设计模式》完整覆盖GoF讲述的23个设计模式并加以细细研磨。...第25章 访问者模式(Visitor) 附录A常见面向对象设计原则 附录BUML简介 参考文献

    设计模式那点事

    包括责任链模式(Chain of Responsibility)、命令模式(Command)、解释器模式(Interpreter)、迭代器模式(Iterator)、备忘录模式(Memento)、...Strategy)、模板方法模式(Template Method)和访问者模式(Visitor)...

    21-Visitor.rar

    在"21-Visitor.rar"的压缩包文件中,可能包含了关于如何实现和应用访问者模式的实例代码、讲解文档或示例项目,通过学习这些内容,开发者可以深入理解访问者模式的精髓,并在实际开发中灵活运用,提高代码的可维护性...

    领悟设计模式

    ### 领悟设计模式——Template Method与Visitor模式解析 #### 概述 本文源自C/C++ User's Journal,这是一本曾广受好评的专业C++杂志中的一个栏目。通过情景对话的形式,深入浅出地介绍了C++中的两种重要设计模式...

    C++设计模式精解

    7. 深入理解与实践:文档中提到作者也会在C++实现源码中深入解析State模式,探讨双分派在Visitor模式中的应用,以及为什么使用设计模式等,强调了实践和深入理解设计模式的重要性。 8. 学习过程的思考:作者将学习...

    23种面向对象设计模式

    3. **行为型模式**(Behavioral Patterns):关注对象之间的责任分配和通信,包括策略模式(Strategy)、模板方法模式(Template Method)、迭代器模式(Iterator)、观察者模式(Observer)、访问者模式(Visitor)...

Global site tag (gtag.js) - Google Analytics