Java设计模式(23)——行为模式之访问者模式(Visitor)
一、概述
概念

作用于某个对象群中各个对象的操作。它可以使你在不改变这些对象本身的情况下,定义作用于这些对象的新操作。
引入
试想这样一个场景,在一个Collection中放入了一大堆的各种对象的引用,取出时却需要根据这些对象的不同具体类型执行不同操作,那我们有如下方案:
public void show(Collection c) {
Iterator it = c.iterator();
while (it.hasNext()) {
Object o = it.next();
if (o instanceof Integer) {
// Integer对应的操作
} else if (o instanceof String) {
// String对应的操作
} else if (o instanceof Collection) {
// Collection对应的操作
} else {
// 省略若干个else if
}
}
}
就不分析说这段代码到底有什么问题了,我们直接引入解决办法:访问者模式——把数据结构和数据结构之上的操作解耦
UML简图

结构

角色
抽象访问者:声明多个访问操作
具体访问者:实现接口中操作
抽象节点:声明接受操作,接收访问者作为一个参量
具体节点:实现接受操作
结构对象:可以遍历结构中所有元素
二、实践
根据角色给出示意性代码:
抽象访问者
/**
* 访问者接口
*
* @author Administrator
**/
public interface Visitor {
/**
* 访问NodeA
* @param node 访问结点
*/
void visit(NodeA node); /**
* 访问NodeB
* @param node 访问结点
*/
void visit(NodeB node);
}
具体访问者
/**
* 访问者A
*
* @author Administrator ON 2017/11/3
**/
public class VisitorA implements Visitor{
@Override
public void visit(NodeA nodeA) {
System.out.println(nodeA.operationA());
} @Override
public void visit(NodeB nodeB) {
System.out.println(nodeB.operationB());
}
}
/**
* 访问者B
*
* @author Administrator ON 2017/11/3
**/
public class VisitorB implements Visitor{
@Override
public void visit(NodeA nodeA) {
System.out.println(nodeA.operationA());
} @Override
public void visit(NodeB nodeB) {
System.out.println(nodeB.operationB());
}
}
抽象结点
/**
* 抽象节点
*
* @author Administrator ON 2017/11/3
**/
public abstract class Node {
public abstract void accept(Visitor v);
}
具体结点
/**
* A结点
*
* @author Administrator ON 2017/11/3
**/
public class NodeA extends Node{
@Override
public void accept(Visitor v) {
v.visit(this);
}
public String operationA() {
return "A结点特有方法";
}
}
/**
* B结点
*
* @author Administrator ON 2017/11/3
**/
public class NodeB extends Node{
@Override
public void accept(Visitor v) {
v.visit(this);
}
public String operationB() {
return "B结点特有方法";
}
}
结构对象
/**
* 结构对象
*
* @author Administrator ON 2017/11/3
**/
public class ObjectStructure {
private List<Node> nodeList;
private Node node; public ObjectStructure() {
nodeList = new ArrayList<>();
} /**
* 执行访问操作
*/
public void action(Visitor v) {
Iterator<Node> iterator = nodeList.iterator();
while (iterator.hasNext()) {
node = iterator.next();
node.accept(v);
}
} /**
* 增加一个新结点
* @param node 待增加的结点
*/
public void add(Node node) {
nodeList.add(node);
}
}
客户端简单操作:
public static void main(String[] args) {
// 新建并初始化结构对象
ObjectStructure os = new ObjectStructure();
os.add(new NodeA());
os.add(new NodeB());
// 新增访问者并访问
Visitor v1 = new VisitorA();
os.action(v1);
}

我们根据访问者的核心,把上面提出的问题使用访问者模式进行改进
访问者接口——通过重载实现了不同类型的不同访问
/**
* 集合访问者接口
*
* @author Administrator
**/
public interface CollectionVisitor {
/**
* 访问String元素
* @param se String类型元素
*/
void visit(StringElement se); /**
* 访问Integer类型元素
* @param ie Integer类型元素
*/
void visit(IntegerElement ie);
}
具体访问者
/**
* 具体访问者
*
* @author Administrator ON 2017/11/3
**/
public class ConcreteVisitor implements CollectionVisitor{
@Override
public void visit(StringElement se) {
System.out.println(se.stringValue());
} @Override
public void visit(IntegerElement ie) {
System.out.println(ie.integerValue());
}
}
抽象被访问元素——通过accept接收访问者
/**
* 元素接口
*
* @author Administrator ON 2017/11/3
**/
public interface Element {
/**
* 接收访问
* @param visitor 访问者
*/
void accept(CollectionVisitor visitor);
}
具体元素
/**
* String类型的元素
*
* @author Administrator ON 2017/11/3
**/
public class StringElement implements Element{
@Override
public void accept(CollectionVisitor visitor) {
visitor.visit(this);
}
public String stringValue() {
return "StringElement";
}
}
/**
* Integer类型元素
*
* @author Administrator ON 2017/11/3
**/
public class IntegerElement implements Element{
@Override
public void accept(CollectionVisitor visitor) {
visitor.visit(this);
}
public Integer integerValue() {
return 1;
}
}
客户端使用
/**
* 客户端
* @author Administrator
**/
public class Client { public static void main(String[] args) {
// 创建需要访问的元素对象集合
List<Element> elementList = new ArrayList<>();
elementList.add(new StringElement());
elementList.add(new IntegerElement());
// 创建访问者
CollectionVisitor visitor = new ConcreteVisitor();
// 接收访问者开始访问
for (Element element : elementList) {
element.accept(visitor);
}
}
}
三、改进与思考
应当指明,只有在系统十分稳定,确定不会加入新结点时使用,因为加入新节点将无法做到“开闭原则”
特别注意,访问者模式是解决了不停判断的问题!可以直接根据传入的参数进行判断和接收(回传球)
浅显的栗子请参见:http://ifeve.com/visitor-design-pattern-in-java-example-tutorial/
Java设计模式(23)——行为模式之访问者模式(Visitor)的更多相关文章
- Java 设计模式系列(二三)访问者模式(Vistor)
Java 设计模式系列(二三)访问者模式(Vistor) 访问者模式是对象的行为模式.访问者模式的目的是封装一些施加于某种数据结构元素之上的操作.一旦这些操作需要修改的话,接受这个操作的数据结构则可以 ...
- 设计模式之第20章-访问者模式(Java实现)
设计模式之第20章-访问者模式(Java实现) “嘿,你脸好红啊.”“精神焕发.”“怎么又黄了?”“怕冷,涂的,涂的,蜡.”“身上还有酒味,露馅了吧,原来是喝酒喝的啊.”“嘿嘿,让,让你发现了,今天来 ...
- Java设计模式之十一种行为型模式(附实例和详解)
Java经典设计模式共有21中,分为三大类:创建型模式(5种).结构型模式(7种)和行为型模式(11种). 本文主要讲行为型模式,创建型模式和结构型模式可以看博主的另外两篇文章:J设计模式之五大创建型 ...
- 设计模式学习-使用go实现访问者模式
访问者模式 定义 优点 缺点 适用范围 代码实现 什么是 Double Dispatch 参考 访问者模式 定义 访问者模式(Visitor):表示一个作用于某对象结构中的各元素的操作.它使你可以在不 ...
- Java设计模式(三) 抽象工厂模式
原创文章,同步发自作者个人博客,转载请注明出处 http://www.jasongj.com/design_pattern/abstract_factory/ 抽象工厂模式解决的问题 上文<工厂 ...
- Java设计模式(十二) 策略模式
原创文章,同步发自作者个人博客,http://www.jasongj.com/design_pattern/strategy/ 策略模式介绍 策略模式定义 策略模式(Strategy Pattern) ...
- Java设计模式(二) 工厂方法模式
本文介绍了工厂方法模式的概念,优缺点,实现方式,UML类图,并介绍了工厂方法(未)遵循的OOP原则 原创文章.同步自作者个人博客 http://www.jasongj.com/design_patte ...
- Java设计模式(一) 简单工厂模式不简单
摘要:本文介绍了简单工厂模式的概念,优缺点,实现方式,以及结合Annotation和反射的改良方案(让简单工厂模式不简单).同时介绍了简单工厂模式(未)遵循的OOP原则.最后给出了简单工厂模式在JDB ...
- Java设计模式(十一) 享元模式
原创文章,同步发自作者个人博客 http://www.jasongj.com/design_pattern/flyweight/.转载请注明出处 享元模式介绍 享元模式适用场景 面向对象技术可以很好的 ...
- Java设计模式(14)责任链模式(Chain of Responsibility模式)
Chain of Responsibility定义:Chain of Responsibility(CoR) 是用一系列类(classes)试图处理一个请求request,这些类之间是一个松散的耦合, ...
随机推荐
- Python深入学习之《Fluent Python》 Part 1
Python深入学习之<Fluent Python> Part 1 从上个周末开始看这本<流畅的蟒蛇>,技术是慢慢积累的,Python也是慢慢才能写得优雅(pythonic)的 ...
- sublime text html5开发学习 插件篇记录
1.第一步先按照 Package Control,具体步骤自行百度,Google. 2. view in browser 默认的快捷键应该是这样的,我用的是IE浏览器.所以ctrl+alt+i 即可让 ...
- bzoj3609 [Heoi2014]人人尽说江南好
Description 小 Z 是一个不折不扣的 ZRP(Zealot Round-game Player,回合制游戏狂热玩家),最近他 想起了小时候在江南玩过的一个游戏. 在过去,人们是要边玩 ...
- LAMP的简介
**********************补2016年5月23日的博客************************* LAMP--------Linux Apache MySQL PHP ...
- 10、SpringBoot-CRUD登陆拦截
1.前端页面的设置 index.html <input type="text" class="form-control" name="usern ...
- CentOS7 安装配置备忘录
1. 安装 epel 源 $ sudo yum install epel-release.noarch 如果想强制服务器使用 tuna 的镜像,可以如下修改 /etc/yum.repos.d/epel ...
- P2213 [USACO14MAR]懒惰的牛The Lazy Cow_Sliver
P2213 [USACO14MAR]懒惰的牛The Lazy Cow_Sliver 最大化一个子矩阵的和. 我们如何去做,dp和贪心呀! 大体题意:给定一个正方形,然后在正方形中求出一个大小已经给定的 ...
- Grunt中批量无损压缩图片插件--Grunt-contrib-imagemin
Photoshop 切出的图片,无论是 PNG 还是 JPEG/JPG 格式,都含有许多相关信息,又或多余的颜色值,这些信息和颜色值,对网页前端并没有用处,反而增加图片大小,所以 Google Pag ...
- 让NSArray数组中每个对象都调用的方法
1. [array valueForKey:@"title"]; //Returns an array containing the results of invoking val ...
- php auto_prepend_file和auto_append_file的妙用
这是一个鲜为人知的设置! auto_prepend_file 和 auto_append_file 是在php.ini中进行配置的2个参数,auto_prepend_file 表示在php程序加载第一 ...