我猜想许多人都知道访问者设计模式,这种模式在“四人帮”的那本可复用面向对象软件基础的书被描述过。这个模式自身其实一点也不复杂(和以往的其他设计模式一样)。 

如上图所示:

我知道这个模式很久了,但是我至今都不需要它。Java通过本地方式处理多态:方法被调用时是基于调用这个方法的对象运行时的类型,而是不是基于调用对象编译时的类型。

interface Animal{
void eat();
}
public class Dog implements Animal {
public void eat() {
System.out.println("Gnaws bones");
}
}
Animal a = new Dog();
a.eats(); // Prints "Gnaws bones"

然而,以上的方式对于参数类型却无法有效的运行。

  public class Feeder {
public void feed(Dog d) {
d.eat();
}
public void feed(Cat c) {
c.eat();
}
} Feeder feeder = new Feeder();
Object o = new Dog();
feeder.feed(o); // Cannot compile!

这个问题被称之为双重派发,因为它既要求被调用的方法既基于调用方法的实例,同时也基于方法的参数类型。而对于参数类型而言,Java不是基于本地化方式来处理。为了能够编译通过,下面的代码是必须的:

if (o instanceof Dog) {
feeder.feed((Dog) o);
} else if (o instanceof Cat) {
feeder.feed((Cat) o);
} else {
throw new RuntimeException("Invalid type");
}

随着更多重载方法的出现,情况也会变得更加复杂——方法中出现更多的参数,复杂度也会呈指数级别提高。在维护阶段,添加更多的重载的方法需要阅读所有代码,如果程序填充了太多不必要的代码需就要去更新它。多个参数通过嵌套多个if来实现,这对于维护会变得更加糟糕。访问者模式是一种优雅的方式来解决以上同样的效果,不使用多个if,而使用Animal类中的一个单独的方法来作为解决的代价。

public interface Animal {
void eat();
void accept(Visitor v);
} public class Cat {
public void eat() { ... }
public void accept(Visitor v) {
v.visit(this);
}
} public class Dog {
public void eat() { ... }
public void accept(Visitor v) {
v.visit(this);
}
} public class FeederVisitor {
public void visit(Cat c) {
new Feeder().feed(c);
}
public void visit(Dog d) {
new Feeder().feed(d);
}
}

好处:

  • 没有逻辑的评价出现
  • 只是在Animal和FeederVisitor之间建立依赖,FeederVisitor中只限于visit方法
  • 按照推论,当添加新的Animal子类的时候,Feeder类可以保持不变
  • 当添加一个新的Animal子类的时候,FeederVisitor类实现一个额外的方法去处理它即可
  • 其他的横切逻辑也可以遵循相同的模式,比如:一个来教动物新把戏的训练特征
  • 对于一些简单的例子使用如此长的代码似乎有杀鸡用宰牛刀的感觉。然而,我的经验教会了我像上面简单的填充代码,当随着项目的发展业务逻辑变负责是致命的。

原文链接: frankel
翻译: ImportNew.com - 潘 凌霄
译文链接: http://www.importnew.com/11319.html

Visitor设计模式的更多相关文章

  1. visitor设计模式记录

    数据类型通过枚举来区分是一种简单实用的做法. 缺点是使用的时候需要通过if .switch 去判断什么类型执行什么分支操作,说是缺点其实也要看具体场景.不过如果if代码多会导致代码很长是肯定的. 复杂 ...

  2. TypeScript Visitor设计模式

    以下翻译脑袋的VBF项目,试试看TypeScript能否重写. class RegExpr {     Accept<T>(convert: Converter<T>) {   ...

  3. 浅谈设计模式-visitor访问者模式

    先看一个和visitor无关的案例.假设你现在有一个书架,这个书架有两种操作,1添加书籍2阅读每一本书籍的简介. //书架public class Bookcase { List<Book> ...

  4. Java设计模式学习资源汇总

    本文记录了Java设计模式学习书籍.教程资源.此分享会持续更新: 1. 设计模式书籍 在豆瓣上搜索了一把,发现设计模式贯穿了人类生活的方方面面.还是回到Java与程序设计来吧. 打算先归类,再浏览,从 ...

  5. java设计模式---访问者模式

      Java深入到一定程度,就不可避免的碰到设计模式这一概念,了解设计模式,将使自 己对java中的接口或抽象类应用有更深的理解.设计模式在java的中型系统中应用广 泛,遵循一定的编程模式,才能使自 ...

  6. AOP 的利器:ASM 3.0 介绍

    引言 什么是 ASM ? ASM 是一个 Java 字节码操控框架.它能被用来动态生成类或者增强既有类的功能.ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态 ...

  7. 【java】字节码操作技术

    asm.javassist.cglib. 1.asm 比较底层,使用的visitor设计模式. 官网:https://asm.ow2.io/ 2.javassist 官网:http://www.jav ...

  8. Java ASM 技术简介

    什么是ASM ASM 是一个 Java 字节码操控框架.它能被用来动态生成类或者增强既有类的功能.ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为. ...

  9. [转]Spring中property-placeholder的使用与解析

    我们在基于spring开发应用的时候,一般都会将数据库的配置放置在properties文件中. 代码分析的时候,涉及的知识点概要: NamespaceHandler 解析xml配置文件中的自定义命名空 ...

随机推荐

  1. Codeforces 797 D. Broken BST

    D. Broken BST http://codeforces.com/problemset/problem/797/D time limit per test 1 second memory lim ...

  2. vector的哈希值 Codecraft-17 and Codeforces Round #391 (Div. 1 + Div. 2, combined) C

    http://codeforces.com/contest/757/problem/C 题目大意:有n个导管,每个体育馆有k种神奇宝贝,然后所有的n个体育馆中,一共有m中神奇宝贝.可知,每个神奇宝贝中 ...

  3. 动态规划:插头DP

    这种动归有很多名字,插头DP是最常见的 还有基于连通性的动态规划 轮廓线动态规划等等 超小数据范围,网格图,连通性 可能算是状态压缩DP的一种变式 以前我了解的状压DP用于NP难题的小数据范围求解 这 ...

  4. [Luogu 1351] NOIP2014 联合权值

    [Luogu 1351] NOIP2014 联合权值 存图,对于每一个点 \(u\),遍历它的所有邻接点.以 \(u\) 为中转点的点对中,\((x,y)\) 的联合权值 \(w_x \cdot w_ ...

  5. CSS3之伪元素选择器和伪类选择器

    伪类选择器,和一般的DOM中的元素样式不一样,它并不改变任何DOM内容.只是插入了一些修饰类的元素,这些元素对于用户来说是可见的,但是对于DOM来说不可见.伪类的效果可以通过添加一个实际的类来达到. ...

  6. 《JavaScript 实战》:实现图片幻滑动展示效果

    滑动展示效果主要用在图片或信息的滑动展示,也可以设置一下做成简单的口风琴(Accordion)效果.这个其实就是以前写的图片滑动展示效果的改进版,那是我第一篇比较受关注的文章,是时候整理一下了. 有如 ...

  7. 无废话JavaScript(下)

    五.函数式 这个可不是JavaScript的发明,它的发明人已经死了,而他的这个发明还在困扰着我们……如同爱迪生的灯泡还在照耀着我们. 其实函数式语言很简单,它就是一种与命令式语言同样“完备”的语言实 ...

  8. jq消除网页滚动条

    网页有些时候需要能滚动的效果,但是不想要滚动条,我就遇到了这样的需求.自己用jq写了一个垂直滚动条. 纯css也可以实现 .box::-webkit-scrollbar{display:none} 但 ...

  9. 【读书笔记::深入理解linux内核】内存寻址

    我对linux高端内存的错误理解都是从这篇文章得来的,这篇文章里讲的 物理地址 = 逻辑地址 – 0xC0000000:这是内核地址空间的地址转换关系. 这句话瞬间让我惊呆了,根据我的CPU的知识,开 ...

  10. Linux 上配置 NTP SERVER

    在CENTOS 6.2上面安装配置NTP SERVER 安装NTP:yum install ntp 配置时间源vi /etc/ntp.confserver 210.72.145.44server nt ...