设计模式(十三)Visitor模式
Visitor模式可以用来把数据结构与处理分离开。通俗来说就是编写一个访问者类来访问数据结构中的元素,并把对各元素的处理交给访问者类。这样,当需要增加新的处理时,只需要编写新的访问者,然后让数据结构可以接受访问者的访问即可。
下面先看示例程序的类图。

在示例程序中,使用Composite模式中用到了那个文件和文件夹的例子作为访问者要访问的数据结构。访问者会访问由文件和文件夹构成的数据结构,然后显示出文件和文件夹的一览。
package bigjunoba.bjtu.visitor;
public abstract class Visitor {
public abstract void visit(File file);
public abstract void visit(Directory directory);
}
Visitor类是表示访问者的抽象类。访问者依赖于它所访问的数据结构(即File类和Directory类)。这里visit方法用到了重载,分别表示了用于访问文件和文件夹的方法。
package bigjunoba.bjtu.visitor;
public interface Element {
public abstract void accept(Visitor v);
}
Element接口是接受访问者的访问的接口。accept方法的参数是访问者Visitor类。
package bigjunoba.bjtu.visitor;
import java.util.Iterator;
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<Element> iterator() throws FileTreatmentException { // 生成Iterator
throw new FileTreatmentException();
}
public String toString() { // 显示字符串
return getName() + " (" + getSize() + ")";
}
}
Entry类与Composite模式中的Entry类是一样的,不过该Entry类实现了Element接口,这是为了让Entry类适用于Visitor模式。实际上实现Element接口中声明的抽象方法accept的是Entry类的子类。
add方法仅对Directory类有效,因此在Entry类中,让它简单地报错。同样,用于获取Iterator的iterator方法也仅对Directory类有效,也让它简单地报错。
package bigjunoba.bjtu.visitor;
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);
}
}
File类中要注意的是它是如何实现accept接口的。accept方法的参数是Visitor类,然后调用Visitor类的visit方法,由于这里的this是File类的实例,所以调用的是第一个visit方法。
package bigjunoba.bjtu.visitor; import java.util.Iterator;
import java.util.ArrayList; public class Directory extends Entry {
private String name; // 文件夹名字
private ArrayList<Entry> dir = new ArrayList<Entry>(); // 目录条目集合
public Directory(String name) { // 构造函数
this.name = name;
}
public String getName() { // 获取名字
return name;
}
public int getSize() { // 获取大小
int size = 0;
Iterator<Entry> 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);
}
}
同File类一样,调用visit的第一个方法,告诉访问者“当前正在访问的是Directory类的实例”。
package bigjunoba.bjtu.visitor;
import java.util.Iterator;
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;
}
}
ListVisitor类是访问数据结构并显示元素一览。visit(Directory directory)方法,遍历文件夹中的所有目录条目并调用它们各自的accept方法,然后accept方法调用visit方法,visit方法又会调用accept方法,这样就形成了非常复杂的递归调用。通常的递归调用是某个方法调用自身,在Visitor模式中,则是accept方法与visit方法之间相互调用。
package bigjunoba.bjtu.visitor;
public class FileTreatmentException extends RuntimeException {
public FileTreatmentException() {
}
public FileTreatmentException(String msg) {
super(msg);
}
}
处理异常类。
package bigjunoba.bjtu.visitor;
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 yuki = new Directory("yuki");
Directory hanako = new Directory("hanako");
Directory tomura = new Directory("tomura");
usrdir.add(yuki);
usrdir.add(hanako);
usrdir.add(tomura);
yuki.add(new File("diary.html", 100));
yuki.add(new File("Composite.java", 200));
hanako.add(new File("memo.tex", 300));
tomura.add(new File("game.doc", 400));
tomura.add(new File("junk.mail", 500));
rootdir.accept(new ListVisitor());
} catch (FileTreatmentException e) {
e.printStackTrace();
}
}
}
这里和Composite模式做比较,Composite模式中调用printList方法来显示文件夹中的内容,该方法已经在Directory类(即表示数据结构的类)中实现了。而在Visitor模式中是在访问者中显示文件夹中的内容。这是因为显示文件夹中的内容业数据对数据结构中的各元素进行的处理。
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/yuki (300)
/root/usr/yuki/diary.html (100)
/root/usr/yuki/Composite.java (200)
/root/usr/hanako (300)
/root/usr/hanako/memo.tex (300)
/root/usr/tomura (900)
/root/usr/tomura/game.doc (400)
/root/usr/tomura/junk.mail (500)
运行结构和Composite模式中的运行结果一样。












设计模式(十三)Visitor模式的更多相关文章
- 设计模式 ( 十三 ) 命令模式Command(对象行为型)
设计模式 ( 十三 ) 命令模式Command(对象行为型) 1.概述 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需 ...
- 设计模式之visitor模式,人人能懂的有趣实例
设计模式,现在在网上随便搜都一大堆,为什么我还要写"设计模式"的章节呢? 两个原因: 1.本人觉得这是一个有趣的设计模式使用实例,所以记下来: 2.看着设计模式很牛逼,却不知道怎么 ...
- 设计模式之——visitor模式
visitor模式,又叫访问者模式,把结构和数据分开,编写一个访问者,去访问数据结构中的元素,然后把对各元素的处理全部交给访问者类.这样,当需要增加新的处理时候,只需要编写新的 访问者类,让数据结构可 ...
- 设计模式之Visitor模式(笔记)
訪问者模式:表示一个作用于某个对象结构中的各元素操作.它使你能够不改变各元素的类的前提下定义作用于这些元素的新操作. 首先定义一个visitor抽象类,为每一个详细类声明一个visit操作 publi ...
- 设计模式:visitor模式
核心:将数据结构和数据的处理分开 注意:注意函数的参数传递和调用关系 例子: class Element; class Visitor { public: virtual void Visit(Ele ...
- [设计模式] 23 访问者模式 visitor Pattern
在GOF的<设计模式:可复用面向对象软件的基础>一书中对访问者模式是这样说的:表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作.访问 ...
- 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern)
原文:乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) 作者:webabc ...
- 设计模式:基于线程池的并发Visitor模式
1.前言 第二篇设计模式的文章我们谈谈Visitor模式. 当然,不是简单的列个的demo,我们以电商网站中的购物车功能为背景,使用线程池实现并发的Visitor模式,并聊聊其中的几个关键点. 一,基 ...
- 设计模式---行为变化模式之访问器模式(Visitor)
一:概念 访问者模式,是行为模式之一,它分离对象的数据和行为,使用Visitor模式,可以不修改已有类的情况下,增加新的操作角色和职责. 二:动机 在软件构建的过程中,由于需求的改变,某些类层次结构中 ...
随机推荐
- java架构之路-(分布式zookeeper)zookeeper集群配置和选举机制详解
上次博客我们说了一下zookeeper的配置文件,以及命令的使用https://www.cnblogs.com/cxiaocai/p/11597465.html.我们这次来说一下我们的zookeepe ...
- Spring boot 梳理 - WebMvcConfigurer接口 使用案例
转:https://yq.aliyun.com/articles/617307 SpringBoot 确实为我们做了很多事情, 但有时候我们想要自己定义一些Handler,Interceptor,Vi ...
- Spring Data JPA 梳理 - JPA是什么
总结: JPA是java的标准,不是Spring的标准 java标准中一般通过Meta-INF文件规范开发层面的事情,JPA也不例外,使用persistence.xml JPA定义了Entity 到 ...
- 完整的Android MVP开发之旅
开发背景 最近是在做一个与健身相关的APP,里面有训练器模块基本功能是按照特点动作的演示和描述来引导用户完成训练.在第一个版本时由于没接触过些类项目与功能花了几周的时间大概1500行代码才完成这个功能 ...
- springboot 集成Redis单机
1.redis服务搭建 centos7 搭建redis服务 2.接入相关 pom文件依赖引入 <dependencies> <dependency> <groupId&g ...
- PHP 通过 ReflectionMethod 反射类方法获取注释返回 false 的问题解决
php 通过反射 ReflectionMethod 类来获取类方法的相关信息,其中就包含方法的注释内容. 问题描述 在公司测试环境运行以下代码,如果是 cli 命令行模式运行,正常输出代码注释.如果是 ...
- centos7.x 将普通用户加入sudoers中,获取sudo执行权限
centos7.x 将普通用户加入sudoers中,获取sudo执行权限 0 问题由来 你在提起兴致学习的时候,兴冲冲地往黑洞洞的文本窗口输入下一行: [pan@localhost ~]$ sud ...
- git分支概念与项目中的应用
文档:https://git-scm.com/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%AE%80%E4%BB%8B 分支理解 m ...
- FutureTask是怎样获取到异步执行结果的?
所谓异步任务,就是不在当前线程中进行执行,而是另外起一个线程让其执行.那么当前线程如果想拿到其执行结果,该怎么办呢? 如果我们使用一个公共变量作为结果容器,两个线程共用这个值,那么应该是可以拿到结果的 ...
- 引入外部js
引入外部js应该使用完整标签<script></script>,而使用单标签<script src=“”/>是错误的