《图解设计模式》读书笔记6-1 VISITOR模式
1. Visitor模式简介
Visitor是访问者的意思。
一个数据结构包含多种数据元素,这些数据元素方便存储,却不一定方便使用。因此我们有必要架起一座桥梁,即对数据元素进行处理,方便别人使用。问题来了:数据的处理是放在数据结构类里面还是再造一个类专门用来处理?
如果一个数据结构需要多种处理方式,那每增加一种就要修改数据结构的类,显然不合适。访问者模式就是解决这个问题来的。在这个模式中,数据结构和处理被分离开。我们编写一个访问者,即专门处理数据元素的类来处理数据元素,一旦有新的处理方式,就编写新的访问者类去访问数据结构即可。
2. 示例
接下来的例子以之前的Composite模式为基础编写。为File和Directory类添加一个accept方法,这个方法接收Visitor类,即访问者类。通过这个访问者我们能获得File和Directory类的经过处理的数据展示。
2.1 类图
2.2 代码
ListVisitor
里面的visit(Directory directory)
使用了递归,值得好好体会一下。
public abstract class Visitor {
public abstract void visit(File file);
public abstract void visit(Directory directory);
}
public class ListVisitor extends Visitor {
private String currentdir = "";
public void visit(File file) {
System.out.println(currentdir + "/" + file);
}
public void visit(Directory directory) {
System.out.println(currentdir + "/" + directory);
String savedir = currentdir;
currentdir = currentdir + "/" + directory.getName();
Iterator it = directory.iterator();
while (it.hasNext()) {
Entry entry = (Entry) it.next();
entry.accept(this);
}
currentdir = savedir;
}
}
public interface Element {
public abstract void accept(Visitor v);
}
public abstract class Entry implements Element {
public abstract String getName();
public abstract int getSize();
//添加元素方法,抽象的父类无法添加
public Entry add(Entry entry) throws FileTreatmentException {
throw new FileTreatmentException();
}
//便利元素方法,抽象的父类无法遍历
public Iterator iterator() throws FileTreatmentException {
throw new FileTreatmentException();
}
public String toString() { // 显示字符串
return getName() + " (" + getSize() + ")";
}
}
public class File extends Entry {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
public String getName() {
return name;
}
public int getSize() {
return size;
}
public void accept(Visitor v) {
v.visit(this);
}
}
public class Directory extends Entry {
private String name; // 文件夹名字
private ArrayList dir = new ArrayList(); // 目录条目集合
public Directory(String name) { // 构造函数
this.name = name;
}
public String getName() { // 获取名字
return name;
}
public int getSize() { // 获取大小
int size = 0;
Iterator it = dir.iterator();
while (it.hasNext()) {
Entry entry = (Entry) it.next();
size += entry.getSize();
}
return size;
}
public Entry add(Entry entry) { // 增加目录条目
dir.add(entry);
return this;
}
public Iterator iterator() { // 生成Iterator
return dir.iterator();
}
public void accept(Visitor v) { // 接受访问者的访问
v.visit(this);
}
}
public class Main {
public static void main(String[] args) {
try {
System.out.println("Making root entries...");
Directory rootdir = new Directory("root");
Directory bindir = new Directory("bin");
Directory tmpdir = new Directory("tmp");
Directory usrdir = new Directory("usr");
rootdir.add(bindir);
rootdir.add(tmpdir);
rootdir.add(usrdir);
bindir.add(new File("vi", 10000));
bindir.add(new File("latex", 20000));
rootdir.accept(new ListVisitor());
System.out.println("");
System.out.println("Making user entries...");
Directory xiaoming = new Directory("小明");
Directory xiaohong = new Directory("小红");
Directory xiaohua = new Directory("小花");
usrdir.add(xiaoming);
usrdir.add(xiaohong);
usrdir.add(xiaohua);
xiaoming.add(new File("diary.html", 100));
xiaoming.add(new File("Composite.java", 200));
xiaohong.add(new File("memo.tex", 300));
xiaohua.add(new File("game.doc", 400));
xiaohua.add(new File("junk.mail", 500));
rootdir.accept(new ListVisitor());
} catch (FileTreatmentException e) {
e.printStackTrace();
}
}
}
/////////////////////////////结果////////////////////////////////
Making root entries...
/root (30000)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (0)
Making user entries...
/root (31500)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (1500)
/root/usr/小明 (300)
/root/usr/小明/diary.html (100)
/root/usr/小明/Composite.java (200)
/root/usr/小红 (300)
/root/usr/小红/memo.tex (300)
/root/usr/小花 (900)
/root/usr/小花/game.doc (400)
/root/usr/小花/junk.mail (500)
3. 模式的角色和类图
- Visitor(访问者): 抽象的访问者类,里面声明了供Element调用的访问方法visit。本例中由Visitor类扮演此角色。
- ConcreteVisitor(具体的访问者):具体的访问者类,里面实现了供Element调用的访问方法visit,本例中由ListVisitor类扮演此角色。
- Element(元素):表示Visitor访问的对象,声明了接收访问者的accept方法,参数是Visitor角色,本例中由Element接口扮演此角色。
- ConcreteElement(具体的元素):实现Element定义的接口,本例中由File类和Directory类扮演此角色。
- ObjectStructure(对象结构):负责处理Element角色的集合。ConcreteVisitor角色为每个Element角色都准备了处理方法,在本例中,由Directory类扮演此角色。为了让ConcreteVisitor可以遍历处理每个Element角色,在示例程序中,我们在Directory类中实现了iterator方法。
4. 思路拓展
4.1 双重分发
在代码中,元素接受访问者:element.accept(visitor)
,访问者访问元素visitor.visit(element)
,对比这两个方法,他们是相反的关系,这种消息分发的方式被称为双重分发。
4.2 开闭原则
对扩展开放
对修改关闭
如果要增加功能,要在不修改现有代码的前提下进行扩展。Visitor模式中,如果要扩展数据结构的访问方法,不需要修改数据结构的类, 只需要增加一个访问者类即可。体现了开闭原则。
4.3 难以增加ConcreteElement角色
Visitor模式很容易增加ConcreteVisitor角色,却很难应对ConcreteElement角色的增加。假设我们要增加Entry类的子类Device类,这个类和File类、Directory类是兄弟关系。我们就需要在Visitor类中声明一个visit(Device)方法,所有Visitor类的子类都要实现这个方法。
4.4 Visitor工作所需的条件
Visitor需要从数据结构中获取到足够多的信息才能够工作。如果数据结构向Visitor公开了不该公开的信息,将来对数据结构的改良就会变得非常困难。
《图解设计模式》读书笔记6-1 VISITOR模式的更多相关文章
- HeadFirst设计模式读书笔记(3)-装饰者模式(Decorator Pattern)
装饰者模式:动态地将责任附件到对象上.若要扩展功能,装饰者提东了比继承更有弹性的替代方案. 装饰者和被装饰对象有相同的超类型 你可以用一个或者多个装饰者包装一个对象. 既然装饰者和被装饰对象有相同的超 ...
- HeadFirst设计模式读书笔记--目录
HeadFirst设计模式读书笔记(1)-策略模式(Strategy Pattern) HeadFirst设计模式读书笔记(2)-观察者模式(Observer Pattern) HeadFirst设计 ...
- Head First 设计模式读书笔记(1)-策略模式
一.策略模式的定义 策略模式定义了算法族,分别封装起来,让它们之间可以互换替换,此模式让算法的变化独立使用算法的客户. 二.使用策略模式的一个例子 2.1引出问题 某公司做了一套模拟鸭子的游戏:该游戏 ...
- JavaScript设计模式:读书笔记(未完)
该篇随我读书的进度持续更新阅读书目:<JavaScript设计模式> 2016/3/30 2016/3/31 2016/4/8 2016/3/30: 模式是一种可复用的解决方案,可用于解决 ...
- C#设计模式学习笔记:(21)访问者模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/8135083.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲行为型设计模式的第九个模式--访 ...
- 图解http读书笔记
以前对HTTP协议一知半解,一直不清楚前端需要对于HTTP了解到什么程度,知道接触的东西多了,对于性能优化.服务端的配合和学习中也渐渐了解到了HTTP基础的重要性,看了一些大神对HTTP书籍的推荐,也 ...
- Java设计模式学习笔记(二) 简单工厂模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 正文开始... 1. 简介 简单工厂模式不属于GoF23中设计模式之一,但在软件开发中应用也较为 ...
- Java设计模式学习笔记(三) 工厂方法模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 简介 上一篇博客介绍了简单工厂模式,简单工厂模式存在一个很严重的问题: 就是当系统需要引入 ...
- Java设计模式学习笔记(四) 抽象工厂模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 抽象工厂模式概述 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问 ...
- C#设计模式学习笔记:(23)解释器模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/8242238.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲行为型设计模式的第十一个模式-- ...
随机推荐
- JavaScript如何获得两点之间顺时针旋转的角度
接上回:如何用javascript中的canvas让图片自己旋转 既然能够制定角度让图片旋转,那么现在要做的是自动获得旋转的角度, 让图片能够随着鼠标的方向旋转,让人物能够面朝鼠标的方向: 源代码: ...
- Java-多线程第一篇多线程相关认识(1)
1.单线程进程 如果程序执行某行代码时遇到了阻塞,则程序将会停滞在该处. 2.进程代表着一个程序,程序是静态的,进程是动态的程序. 进程是系统进行资源分配和调度的一个独立单位.关于进程有如下3个特征: ...
- 58-python基础-python3-集合-集合常用方法-删除元素-remove()-discard()-pop()-clear()
删除元素-remove()-discard()-pop()-clear() 1-remove() remove()用于删除一个set中的元素,这个值在set中必须存在,如果不存在的话,会引发KeyEr ...
- python学习第二十九天函数局部变量如何改变外部变量
python函数局部变量如何改变外部变量,之前我说过,局部变量是没办法改变外部变量的,除非局部变量找不到,去外部找,输出变量,使用关键词global 使变量改变外部变量. 1,使用关键词global ...
- Android应用程序开发之图片操作(二)——工程图片资源的加载及OOM的处理
(一)工程图片资源的加载方法 在Android应用程序开发之图片操作(一)中,详细说明了如何操作各种资源图片,只是有的没有附上示例代码,在此,我将针对项目工程中的图片资源的显示加载进行说明.官方说明, ...
- 形象生动的SpringBoot和SpringMVC的区别
spring boot只是一个配置工具,整合工具,辅助工具. springmvc是框架,项目中实际运行的代码 Spring 框架就像一个家族,有众多衍生产品例如 boot.security.jpa等等 ...
- Windows下svn使用教程
SVN简介: 为什么要使用SVN? 程序员在编写程序的过程中,每个程序员都会生成很多不同的版本,这就需要程序员有效的管理代码,在需要的时候可以迅速,准确取出相应的版本. Subversion是什么? ...
- Vue-cli3 环境的搭建
Vue-cli3 环境的搭建 准备 浏览器插件:Vue.js devtools VsCode 和 VsCode 插件 WebStorm Nodejs vue-cli git 起飞 安装vue-cli3 ...
- Java使用对象类型作为方法的返回值
- spring整合消息队列rabbitmq
ps:本文只是简单一个整合介绍,属于抛砖引玉,具体实现还需大家深入研究哈.. 1.首先是生产者配置 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ...