Java 的双重分发与 Visitor 模式
双重分发(Double Dispatch)
什么是双重分发?
谈起面向对象的程序设计时,常说起的面向对象的「多态」,其中关于多态,经常有一个说法是「父类引用指向子类对象」。
这种父类的引用指向子类对象的写法类似下面这种:
Animal animal = new Dog();
animal.bark();
另一种常用的形式是
public class Keeper {
public void say(Animal a) {
System.out.println("Animal say");
}
public void say(Dog dog) {
System.out.println("dog say");
}
}
Animal animal = new Animal();
Animal dog = new Dog();
Keeper keeper = new Keeper();
keeper.say(animal);
keep.say(dog);
那上面的keeper调用两次say的时候,会输出什么内容呢?会调用到两个不同的方法吗?
实际上在这两次调用的时候,都会调用到say(Animal a)这个方法。由于这些内容在编译期就能确实下来,这就是 Java 的 静态分发。
从上面的图我们看到,对于两次调用生成的字节码,确实都指向了say(Animal a)这个方法,运行时直接执行方法,输出了对应的内容。
那对应的animal.bark() 为什么最终会调用到 dog 类的方法?这是在运行时确定具体的方法接收者的类型并执行。这就是所谓的动态分发,在运行时确定具体的方法,实现面向对象的多态。
分发(Dispatch)
分发就是指最终确定一个要执行的方法的过程。
对于 Java 等静态语言来说,都是通过 单一分发(Single Dispatch)来进行的方法执行。
比如这样一行代码
dog.eat(new Bone())
最终执行要选择的eat方法,只会根据dog的具体类型选择到对应的方法,而传入的参数并不能影响到对应方法的选择,这种就是 single Dispatch
为了让传入的真实参数,这里就是Bone来真正起到作用,就需要用到Double Dispatch或者叫做Multiple Dispatch
也就是说最终决定调用方法是哪一个的,不仅仅是方法的接收者,还受参数类型的决定。
Visitor 模式
在GoF 的设计模式中,Visitor 模式就使用到了Double Dispatch 达到了调用真实对象的目的。
对于Visitor 模式,最常用的例子是树的遍历。比如在处理到节点和树叶时的方式有区别,此归通过 visitor 的双重分发,实现对于不同的 Element ,执行不同的内容。
代码类似这样:
node.accept(new ConcreateVisitor());
leaf.accept(new ConcreateVisitor());
node 中的 accept方法,会将自己的真实类型再次传递回visitor
public void accept(Visitor v) {
v.visit(this);
}
此时,在visitor中,就能根据真实的类型来调用具体的方法,对应node 和 leaf 分别有类似这样的方法:
public void visit(Node n);
public void visit(Leaf l);
Visitor 总结起来一般是包含 visitor 接口,在visitor 接口中,包含各个即将被访问的 Element对象的处理逻辑。在 各个Element 的具体实现中,再将自己的类型传递回visitor 进行二次分发,实现确切逻辑的调用。
在Tomcat中的应用
Visitor 在Tomcat中也有应用,典型的是解析EL表达式。
比如org.apache.el.parser.Node
这个类中包含一个accept(NodeVisitor visitor)方法
实际的 Node 类型有很多,但在真实调用的这个时候,会通过
public void accept(NodeVisitor visitor) throws Exception {
visitor.visit(this);
将真实类型传回visitor, vistor中会调用具体的方法,从而实现参数也能起到决定作用的功能。
public void visit(Node node) throws ELException {
if (node instanceof AstFunction) {
AstFunction funcNode = (AstFunction) node;
Method m = null;
} else if (xxx) {
}
这里一般会声明多个visit方法,然后上面的visit(this)会直接定位到目标方法上。
以上就是 Java 中的各类分发,以及 visitor这种模式通过模式的形式来实现双重分发的效果。
Java 的双重分发与 Visitor 模式的更多相关文章
- Java设计模式(21)访问模式(Visitor者模式)
Visitor定义:作用于某个对象群中各个对象的操作.它可以使你在不改变这些对象本身的情况下,定义作用于这些对象的新操作. 在Java中,Visitor模式实际上是分离了collection结构中的元 ...
- 《图解设计模式》读书笔记6-1 VISITOR模式
目录 1. Visitor模式简介 2. 示例 2.1 类图 2.2 代码 3. 模式的角色和类图 4. 思路拓展 4.1 双重分发 4.2 开闭原则 4.3 难以增加ConcreteElement角 ...
- Visitor模式,Decorator模式,Extension Object模式
Modem结构 Visitor模式 对于被访问(Modem)层次结构中的每一个派生类,访问者(Visitor)层次中都有一个对应的方法. 从派生类到方法的90度旋转. 新增类似的Windows配置函数 ...
- Visitor模式(访问者设计模式)
Visitor ? 在Visitor模式中,数据结构与处理被分离开来.我们编写一个表示"访问者"的类来访问数据结构中的元素, 并把对各元素的处理交给访问者类.这样,当需要增加新的处 ...
- java开发中的23中设计模式详解--大话设计模式
设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...
- java开发中的23中设计模式
设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...
- 设计模式之visitor模式,人人能懂的有趣实例
设计模式,现在在网上随便搜都一大堆,为什么我还要写"设计模式"的章节呢? 两个原因: 1.本人觉得这是一个有趣的设计模式使用实例,所以记下来: 2.看着设计模式很牛逼,却不知道怎么 ...
- Visitor 模式心得
最近读到Visitor模式,还是一知半解的.偶然翻到Uncle Bob对该模式的推导过程,有所心得,和大家分享一下. Uncle Bob 的链接是: http://butunclebob.com/Ar ...
- Java设计模式(22)命令模式(Command模式)
Command模式是最让我疑惑的一个模式,我在阅读了很多代码后,才感觉隐约掌握其大概原理,我认为理解设计模式最主要是掌握起原理构造,这样才对自己实际编程有指导作用.Command模式实际上不是个很具体 ...
随机推荐
- Ubuntu16.04 释放/boot目录空间
查看当前系统内核 $ uname -a Linux linuxidc --generic #-Ubuntu SMP Mon Feb :: UTC x86_64 x86_64 x86_64 GNU/Li ...
- java服务端微信小程序支付
发布时间:2018-10-05 技术:springboot+maven 概述 java微信小程序demo支付只需配置支付一下参数即可运行 详细 代码下载:http://www.demodash ...
- iOS 瀑布流之栅格布局
代码地址如下:http://www.demodashi.com/demo/14760.html 一 .效果预览 二.确定需求 由下面的需求示意图可知模块的最小单位是正方形,边长是屏幕宽除去边距间隔后的 ...
- 首次启动Kafka报Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x00000000c0000000, 1073741824, 0) failed; error='Cannot allocate memory' (errno=12)
首次启动Kafka报错如下: 原因:内存不足,查看启动配置 调小一些:
- jsp路径问题之base
<base href="<%=basePath%>"> base 标记用于指定页面中所有相对路径的基点, 而默认的相对路径的基点是页面所在路径. 测试代码如 ...
- Pinyin4j导读
Pinyin4j是Java中的拼音库,作者李民. 拼音这件事其实非常简单,无非是一种映射.但是拼音中包含的学问很多,很多前贤都研究过汉语拼音,提出了多种拼音方案.随着历史变迁,拼音的形式也在不断发生变 ...
- Delphi单元文件引用名称问题
Delphi新版本的单元文件格式变化了,如windows变成了winapi.windows,如果想在单元引用中使用简称,则需要在工程选项中配置: 这样就可以使用全名或简写来引用单元了.
- 【DeepLearning】Exercise: Implement deep networks for digit classification
Exercise: Implement deep networks for digit classification 习题链接:Exercise: Implement deep networks fo ...
- Linux 日志切割工具cronolog详解
一.前言 二.cronolog 简介 三.cronolog 特点 四.cronolog 安装 五.cronolog 使用 六.cronolog 总结 注,操作系统 CentOS 6.4 x86_64, ...
- DHCP工作过程的六个主要步骤
对于学习DHCP来说,很重要的一部分就是对于DHCP工作过程的理解. DHCP分为两个部分:一个是服务器端,另一个是客户端. 所有客户机的IP地址设定资料都由DHCP服务器集中管理,并负责处理客户端的 ...