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模式实际上不是个很具体 ...
随机推荐
- Zuul Timeouts
19.13 Zuul Timeouts 19.13.1 Service Discovery Configuration If Zuul is using service discovery there ...
- Dubbo架构设计详解(转收藏)
转自:http://shiyanjun.cn/archives/325.html Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合 ...
- rtl-sdr在win7和ubuntu16.04的安装
硬件准备 RTL2832+R820T2 usb dongle x 1 Long wire antenna x 1 USB电视棒的购买: 在淘宝上搜索"软件无线电"或"rt ...
- awbeci网站之技术篇
之前写的一篇关于awbeci网站的使用和介绍,大家可以看看,地址在:http://www.cnblogs.com/zhangwei595806165/p/5245640.html 1.前台 BootS ...
- 通过Parcelable协议传递数据出现系列错误
07-14 00:42:37.414: E/AndroidRuntime(17687): FATAL EXCEPTION: main 07-14 00:42:37.414: E/AndroidRunt ...
- 【LeetCode】173. Binary Search Tree Iterator (2 solutions)
Binary Search Tree Iterator Implement an iterator over a binary search tree (BST). Your iterator wil ...
- LVS负载均衡之持久性连接介绍(session篇)
在实际生产环境中,往往需要根据业务应用场景来设置lvs的会话超时时间以及防session连接丢失的问题提,如在业务支付环节,如若session丢失会导致重复扣款问题,严重影响到安全性,本小节解将会讲到 ...
- magento的一些小技巧(转)
1.加载某个attribute: $attributeCode=Mage::getModel('catalog/resource_eav_attribute') ...
- httpd: Could not reliably determine the server's fully qualified domain name(转)
ttpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for Ser ...
- golang学习笔记 ---面向并发的内存模型
Go语言是基于消息并发模型的集大成者,它将基于CSP模型的并发编程内置到了语言中,通过一个go关键字就可以轻易地启动一个Goroutine,与Erlang不同的是Go语言的Goroutine之间是共享 ...