基本概念

Visitor

  • 封装一些作用于数据结构中的各元素的操作,不同的操作能够借助新的visitor实现。减少了操作间的耦合性
  • 訪问者能够将数据结构对数据的操作解耦,使得添加对数据结构的操作不须要取改动数据结构,也不必去改动原有的操作,而运行时再定义新的Visitor时闲着即可了(在操作加入上易拓展)

模式中角色分工

  • Visitor:抽象訪问者,在重载的visit函数中声明訪问者能够訪问的对象
  • Concrete Visitor:实现一个訪问者对于一个详细的元素的操作
  • Element:抽象元素,声明具有訪问该类型元素权限的訪问者的类型(通常是抽象类型)。提供重载的accept函数赋予权限
  • Concrete Element:实现accept方法,基本上是模板化的visitor.visit(this)
  • Object Structure:容纳多种类型也许不同。接口或者不同的元素的集合。

例讲Visitor的实现

先是一个简单的样例,展现一个最主要的简陋的Visitor

既然在春招季。我们举个简历筛选的样例,投简历的都是写本科生、专科生。还有硕士生、高职啊…为了简单就先取前两者。求职者的简历作为Element实现例如以下:

abstract class Student {
   //提供对于数据域基本操作的函数
private String name;
private String university;
private String rating;
//让指定的visitor获得操作该对象的权限
public abstract void accept(Visitor visitor); public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getUniversity() {
return university;
} public void setUniversity(String university) {
this.university = university;
} public String getRating() {
return rating;
} public void setRating(String rating) {
this.rating = rating;
}
} class Bachelor extends Student{ @Override
public void accept( Visitor visitor ) {
visitor.visit( this );
}
} class College extends Student{ @Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}

由于我们仅仅定义了两种学生,所以接口提供了对于两种Element訪问

interface Visitor{
public void visit ( Bachelor bachelor );
public void visit ( College college );
}

首先筛选简历我们看一下大家的简历都什么样子,那么须要一个ShowVisitor:

class ShowVisitor implements Visitor {

    @Override
public void visit(Bachelor bachelor) {
System.out.println("A bachelor\n");
//TODO 可能会有一些特异的操作,我们为了简单就省略了
this.printMessage( bachelor );
} @Override
public void visit(College college) {
System.out.println(" a college student!\n");
//TODO 同上
this.printMessage( college );
} public void printMessage ( Student student ){
System.out.println( "Name : " + student.getName()+"\n"
+ "University : " + student.getUniversity()+"\n"
+ "Rating : " + student.getRating() + "\n"
);
}
}

要进行測试,我们首先要构造一个数据集合。也就是角色中相应的ObjectStructure,为了简单我们直接用ArrayList了

public class VisitorEg {
public static void main ( String [] args ){
ArrayList<Student> list = new ArrayList<Student>();
Bachelor bachelor = new Bachelor();
bachelor.setName("llin");
bachelor.setRating("100");
bachelor.setUniversity("Tianjin University"); College college = new College();
college.setUniversity("Tianjin college");
college.setRating("1");
college.setName("dalinge"); list.add ( bachelor );
list.add ( college ); Visitor visitor = new ShowVisitor();
for ( Student student: list ){
student.accept( visitor );
} }
}

那么好像看不出訪问者模式有什么优势啊!!!并且好费事啊,可是由于你将数据结构和对数据的操作分离了(解耦),所以当我想加入新的操作时,不须要改动原有的类,仅仅须要又一次实现一个visitor就能够了。


所以,我们回到这个样例。这么多人报名,那么究竟有多少本科生呢(假设人数够了,可能直接偷懒仅仅面试本科生了),-_-万恶的这样的HR,所以我们须要一个统计的Visitor:

class SumVisitor implements Visitor{

    private int totalBachelor;

    SumVisitor(){
super();
totalBachelor = 0;
} @Override
public void visit(Bachelor bachelor) {
totalBachelor++;
} @Override
public void visit(College college) {
} public int getTotal_bachelor() {
return totalBachelor;
}
} public class VisitorEg {
public static void main ( String [] args ){
ArrayList<Student> list = new ArrayList<Student>();
Bachelor bachelor = new Bachelor();
bachelor.setName("llin");
bachelor.setRating("100");
bachelor.setUniversity("Tianjin University"); College college = new College();
college.setUniversity("Tianjin college");
college.setRating("1");
college.setName("dalinge"); list.add ( bachelor );
list.add ( college ); Visitor visitor = new ShowVisitor();
Visitor visitor1 = new SumVisitor();
for ( Student student: list ){
student.accept( visitor );
student.accept( visitor1);
}
System.out.println( "The total sum of bachelors : "+ ((SumVisitor)visitor1).getTotal_bachelor() );
}
}

达到了要求。却没有改动一行代码,开心!

Visitor应用场景

一定会有的疑问:visitor和iterator的差别:

  • visitor能够訪问不同的对象(仅仅须要在Element定义相应的accept),可是Iterator仅仅能訪问同样的对象。最起码要有同样的接口
  • iterator是不依赖详细实现的,而visitor是依赖详细实现的,由于Visitor会依据訪问的详细的对象来採取相应的操作,而iterator最多仅仅是基于同样的接口的泛化实现。
  • iterator訪问的数据结构的操作和数据并未分离。所以拓展功能起来须要改动,违反了开闭原则单一职责原则

    可是由于訪问者依赖详细实现,而不是依赖抽象。所以违反了依赖倒置原则

优缺点决定的应用场景

  • 符合单一职责原则。功能上具有良好的拓展性,可是由于依赖详细实现违背了详细实现,所以为类的改动带了麻烦。
  • 具有优良的拓展性。仅仅须要实现新的Visitor来满足新的訪问要求。

    由于数据和操作的分离,防止了加入新的操作污染原来的数据结构。

综上

訪问者是一种集中规整模式,特别适合用于大规模重构的项目。在这一个阶段的需求已经非常清晰,原系统的功能点也已经明白。通过訪问者模式能够非常easy把一些功能进行梳理,达到终于目的功能集中化

双分派

首先介绍下面单分派

单分派:一个操作是依据请求者的名称和接收到的參数决定的,在Java有静态绑定动态绑定,各自是通过重载覆写实现的。

双分派:双分派意味着得到运行的操作决定于请求的种类接收者的类型

正相应于訪问者模式

Javac在构建、优化、解析语法树的时候就是採用的是Visitor模式(语法、语义分析阶段)

Java设计模式(三) Visitor(訪问者)模式及多分派场景应用的更多相关文章

  1. 《Java设计模式》之訪问者模式

    訪问者模式是对象的行为模式.訪问者模式的目的是封装一些施加于某种数据结构元素之上的操作.一旦这些操作须要改动的话,接受这个操作的数据结构则能够保持不变. 分派的概念 变量被声明时的类型叫做变量的静态类 ...

  2. JAVA设计模式之:訪问者模式

    訪问者模式: 一个作用于某对象结构中各元素的操作,使你能够在不改变各元素类数据结构的前提下添加作用于这些元素的新操作. 结构对象是訪问者模式必备条件.且这个结构对象必须存在遍历自身各个对象的方法. 适 ...

  3. 设计模式入门之訪问者模式Visitor

    //訪问者模式定义:表示一个作用于某对象结构中的各个元素的操作,它使你能够在不改变各元素类的前提下定义作用于这些元素的新操作. //从定义上看.这个模式跟装饰模式的定义非常类似(动态地给一个对象加入一 ...

  4. JAVA设计模式之 訪问者模式【Visitor Pattern】

    一.概述 訪问者模式是一种较为复杂的行为型设计模式,它包括訪问者和被訪问元素两个主要组成部分.这些被訪问的元素通常具有不同的类型,且不同的訪问者能够对它们进行不同的訪问操作.在使用訪问者模式时,被訪问 ...

  5. 设计模式之十五:訪问者模式(Visitor Pattern)

    訪问者模式(Visitor Pattern)是GoF提出的23种设计模式中的一种,属于行为模式. 据<大话设计模式>中说算是最复杂也是最难以理解的一种模式了. 定义(源于GoF<De ...

  6. 设计模式之二十四:訪问者模式(Visitor)

    訪问者模式: 定义了一个作用于一个类的一些操作,訪问者模式同意在不改变类的前提下添加一些操作. Represent an operation to be performed on the elemen ...

  7. PHP设计模式——訪问者模式

    声明:本系列博客參考资料<大话设计模式>,作者程杰. 訪问者模式表示一个作用于某对象结构中的各元素的操作. 它使你能够在不改变各元素类的前提下定义作用于这些元素的新操作. UML类图: w ...

  8. Java设计模式(14)责任链模式(Chain of Responsibility模式)

    Chain of Responsibility定义:Chain of Responsibility(CoR) 是用一系列类(classes)试图处理一个请求request,这些类之间是一个松散的耦合, ...

  9. Java 设计模式系列(二三)访问者模式(Vistor)

    Java 设计模式系列(二三)访问者模式(Vistor) 访问者模式是对象的行为模式.访问者模式的目的是封装一些施加于某种数据结构元素之上的操作.一旦这些操作需要修改的话,接受这个操作的数据结构则可以 ...

随机推荐

  1. 怎么使用 bat 使用日期时间重命名文件名

    d: rename A.txt "A%date:~0,4%-%date:~5,2%-%date:~8,2%_%time:~0,2%-%time:~3,2%-%time:~6,2%_backu ...

  2. Redis(二):Redis的九大应用场景

    毫无疑问,Redis开创了一种新的数据存储思路,使用Redis,我们不用在面对功能单调的数据库时,把精力放在如何把大象放进冰箱这样的问题上,而是利用Redis灵活多变的数据结构和数据操作,为不同的大象 ...

  3. vim:去掉响铃

    vim在移动字符出界(上下左右)包括按<ESC>建都会响铃,有时候真的很烦. 在网上搜了一下,原来是visualbell来控制的,在vim里使用命令:help visualbell,原来禁 ...

  4. putty设置用key自动登录

    1.在Linux下ssh-keygen -t rsa 生成密钥对 2.把私钥id_isa下载到用scp下载到windows并用puttygen加载并重新保存私钥. 3.在windows下新建快捷方式, ...

  5. Javascript玩转继承(三)

    在前两篇文章中,介绍了构造继承和原型继承.今天把剩下的两种写完,这两种的应用相对于前两种来说应用很少,因此称为是非主流继承方式. 首先,来看非主流继承一:实例继承法.我也不说那么多废话了,既然是非主流 ...

  6. cocos2d-x开发记录:二,基本概念(粒子系统,Scheduler和定时器)

    七,粒子系统 1.导言 术语粒子系统是指计算机图形学技术,它使用大量非常小的精灵或其他图形对象来模拟某些种类的“模糊”现象,于传统渲染技术相比,它很难复制.通常是高度混沌无序的系统,自然现象.化学反应 ...

  7. Xilinx中的xapp1052理解

    xapp1052是xilinx官方给出的一个有关DMA数据传输的样例,用于PC端和FPGA端之间的DMA数据传输.首先需要说的是,xapp1052并不是一个完整的DMA数据传输的终端硬件设计,这在下面 ...

  8. PCIe调试心得_DMA part3

    作者: East  FPGA那点事儿 上一章讲述了PCIe总线如何提高DMA的效率. 本章以服务器常用的4通道1000M以太网卡为例讲述如何实现多个虚拟DMA通道. 1.多通道DMA发 4通道以太网卡 ...

  9. [na]那些OVER的封装(pppoe/ppp/ipsec)

    什么over什么,如pppoe, ppp的封装都在over对象之后,入下图: PPPOE Ipsec

  10. JMeter学习笔记(六)-负载与监听

    1. 场景设计 场景设计的原则:忠于用户实际操作,组合用户的各种操作到场景中来. JMeter场景主要通过线程组设置来完成的,对于复杂场景还需要与逻辑控制器配合完成. 2.场景设置 JMeter线程组 ...