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,这些类之间是一个松散的耦合, ...
随机推荐
- 【深入理解JAVA虚拟机】第二部分.内存自动管理机制.3.垃圾收集器与内存分配策略
1.学习目的 当需要排查各种内存溢出. 内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就需要对这些“自动化”的技术实施必要的监控和调节. Java内存运行时区域的各个部分,其中程序计数 ...
- 我的第一个Python随笔
自学Python也很长时间了,注册博客园写了第一篇随笔.之前想过很多次,但是始终不知道该怎么开始,内容如何,现在想想,随笔嘛,是自己的想法,也自己的实践,又是自己的锻炼.话不多说,开始今天的正式内容. ...
- AngularJS应用的启动和执行过程
启动(startup): <!doctype html> <html ng-app> <head> <script src="http://code ...
- 七.部署war包到Tomcat(基于Centos安装)
1.把war包上传至tomcat的webapps目录下面 2.启动Tomcat,在Tomcat的bin目录下面,运行startup.sh 3.访问项目,如下成功打开项目
- 【转】Android单帧动画Rotate旋转
项目有一个需求,有一个刷新按钮,上面放着一个常见的静止的刷新圆圈,如下图: 一旦用户按了刷新按钮,需要让这个刷新圆圈转动起来,让用户感觉到程序还在运行着,而不是卡死了. 有两个思路,一是将这个图按照旋 ...
- php-------面向对象详解
php面向对象详解 面向对象 对象概念是面向对象技术的核心.在显示世界里我们所面对的事情都是对象,如计算机.电视机.自行车等.在面向对象的程序设计中,对象是一个由信息及对信息进行处理的描述所组成的整体 ...
- 本地测试时修改localhost为自己网站的域名的方法(转载)
做网站的,在本地测试时,所用的地址基本上都是localhost 或者直接用IP地址:127.0.0.1 如果仅仅是用来测试网站内部的程序代码之类的当然没问题,但是如果我们还要测试网站上添加的广告或者统 ...
- WEB测试—用户界面测试
如果有设计稿,当然按照设计稿进行测试:没有设计稿,就参考原型:如果都没有,就按照web大众排版设计要求测试了,当然,还是要产品看过为准. 一下简单总结一下测试的点. 1. 导航测试 很少有用户愿意花时 ...
- CentOS 安装postgresql
CentOS 安装postgresql 添加postgresql官网安装源 在/etc/yum.repos.d目录下新建pgdg-10-centos.repo 文件 [pgdg10] name=P ...
- 十五、详述 IntelliJ IDEA 插件的安装及使用方法
正文 首先,进入插件安装界面: Mac:IntelliJ IDEA -> Preferences -> Plugins; Windows:File -> Settings -> ...