访问者(Visitor)模式:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。访问者模式的结构图如下:
通过上图可以看到他有如下角色:
具体访问者(ConcreteVisitor)角色:实现抽象访问者所声明的接口,也就是抽象访问者所声明的各个访问操作。
抽象元素(Visitable)角色:声明一个接受操作,接受一个访问者对象作为一个参数。
具体元素结点(ConcreteElement)角色:实现抽象结点所规定的接受操作。
数据结构对象(ObjectStructure)角色:可以遍历结构中的所有元素,提供一个接口让访问者对象都可以访问每一个元素。
package visitor; /** * *作者:alaric *时间:2013-9-13下午11:31:28 *描述:抽象访问者 */ public interface Visitor { public void visit(ConcreteElementB able ); public void visit(ConcreteElementA able ); }
package visitor; /** * *作者:alaric *时间:2013-9-13下午11:31:46 *描述:抽象角色元素 */ public interface Visitable { public void accept(Visitor v); }
package visitor; /** * *作者:alaric *时间:2013-9-13下午11:33:29 *描述:具体访问者A */ public class ConcreteVisitorA implements Visitor{ @Override public void visit(ConcreteElementB able) { able.operate(); } @Override public void visit(ConcreteElementA able) { // TODO Auto-generated method stub able.operate(); } }
package visitor; /** * *作者:alaric *时间:2013-9-13下午11:32:55 *描述:具体访问者B */ public class ConcreteVisitorB implements Visitor{ @Override public void visit(ConcreteElementB able) { able.operate(); } @Override public void visit(ConcreteElementA able) { // TODO Auto-generated method stub able.operate(); } }
package visitor; /** * *作者:alaric *时间:2013-9-13下午11:34:02 *描述:具体元素A */ public class ConcreteElementA implements Visitable { @Override public void accept(Visitor v) { v.visit(this); } public void operate(){ System.out.println("ConcreteElementA ...."); } }
package visitor; /** * *作者:alaric *时间:2013-9-13下午11:33:40 *描述:具体元素B */ public class ConcreteElementB implements Visitable { @Override public void accept(Visitor v) { v.visit(this); } public void operate(){ System.out.println("ConcreteElementB ...."); } }
package visitor; import java.util.ArrayList; import java.util.List; /** * *作者:alaric *时间:2013-9-13下午11:34:22 *描述:客户端 */ public class Client { /** * @param args */ public static void main(String[] args) { Visitor v1 = new ConcreteVisitorA(); List<Visitable> list = new ArrayList<>(); list.add(new ConcreteElementA()); list.add(new ConcreteElementB()); for(Visitable able :list){ able.accept(v1); } } }
看了很多设计模式的书,讲访问者设计模式都要提到一个概念“双重分派”,所谓“分派”简单理解就是根据类的特性,特征进行选择,这些选择都是程序语言设计的特征,比如多态(重载,重写)等等,我个人不太注重概念,只要深入掌握面向对象的基础就很好理解了。
设计模式相对其他模式来说结构有点复杂,上面是访问者模式的模拟实现,为了利于学习找了个真实的例子。dom4j里面利用访问者模式来对xml文档进行逐个节点访问,所有文档的对象的父类接口都是Node,对于不同类型的文档对象又做了不同的抽象,所有可能访问的节点如Visitor类中所示,dom4j中定义的Visitor接口如下:
/* * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved. * * This software is open source. * See the bottom of this file for the licence. */ package org.dom4j; /** * <p> * <code>Visitor</code> is used to implement the <code>Visitor</code> * pattern in DOM4J. An object of this interface can be passed to a * <code>Node</code> which will then call its typesafe methods. Please refer * to the <i>Gang of Four </i> book of Design Patterns for more details on the * <code>Visitor</code> pattern. * </p> * * <p> * This <a href="http://www.patterndepot.com/put/8/JavaPatterns.htm">site </a> * has further discussion on design patterns and links to the GOF book. This <a * href="http://www.patterndepot.com/put/8/visitor.pdf">link </a> describes the * Visitor pattern in detail. * </p> * * @author <a href="mailto:james.strachan@metastuff.com">James Strachan </a> * @version $Revision: 1.8 $ */ public interface Visitor { /** * <p> * Visits the given <code>Document</code> * </p> * * @param document * is the <code>Document</code> node to visit. */ void visit(Document document); /** * <p> * Visits the given <code>DocumentType</code> * </p> * * @param documentType * is the <code>DocumentType</code> node to visit. */ void visit(DocumentType documentType); /** * <p> * Visits the given <code>Element</code> * </p> * * @param node * is the <code>Element</code> node to visit. */ void visit(Element node); /** * <p> * Visits the given <code>Attribute</code> * </p> * * @param node * is the <code>Attribute</code> node to visit. */ void visit(Attribute node); /** * <p> * Visits the given <code>CDATA</code> * </p> * * @param node * is the <code>CDATA</code> node to visit. */ void visit(CDATA node); /** * <p> * Visits the given <code>Comment</code> * </p> * * @param node * is the <code>Comment</code> node to visit. */ void visit(Comment node); /** * <p> * Visits the given <code>Entity</code> * </p> * * @param node * is the <code>Entity</code> node to visit. */ void visit(Entity node); /** * <p> * Visits the given <code>Namespace</code> * </p> * * @param namespace * is the <code>Namespace</code> node to visit. */ void visit(Namespace namespace); /** * <p> * Visits the given <code>ProcessingInstruction</code> * </p> * * @param node * is the <code>ProcessingInstruction</code> node to visit. */ void visit(ProcessingInstruction node); /** * <p> * Visits the given <code>Text</code> * </p> * * @param node * is the <code>Text</code> node to visit. */ void visit(Text node); } /* * Redistribution and use of this software and associated documentation * ("Software"), with or without modification, are permitted provided that the * following conditions are met: * * 1. Redistributions of source code must retain copyright statements and * notices. Redistributions must also contain a copy of this document. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The name "DOM4J" must not be used to endorse or promote products derived * from this Software without prior written permission of MetaStuff, Ltd. For * written permission, please contact dom4j-info@metastuff.com. * * 4. Products derived from this Software may not be called "DOM4J" nor may * "DOM4J" appear in their names without prior written permission of MetaStuff, * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd. * * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org * * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved. */dom4j里面有个缺省的访问者(Visitor)的实现VisitorSupport,我们解析一个文档只需继承这个类,然后重写visit方法即可。一个简单的类图表示dom4j是怎么利用visitor设计模式的,如下图:
<?xml version="1.0" encoding="UTF-8"?> <table name="test"> <rows> <row> <id>1</id> <test>Test</test> </row> <row> <id>2</id> <test>Test2</test> </row> </rows </table>我们写个客户端测试,为了简单,把Visitor作为内部类,直接就一个类完成,代码如下:
package com.alaric.dom4j; import java.io.File; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.VisitorSupport; import org.dom4j.io.SAXReader; public class Dom4jTest { public class MyVisitor extends VisitorSupport { public void visit(Attribute node){ System.out.println("属性 : "+node.getName()+" = "+node.getValue()); } public void visit(Element node){ if(node.isTextOnly()){ System.out.println("节点: "+node.getName()+" = "+node.getText()); }else{ System.out.println("节点:"+node.getName()); } } } public static void main(String[] args) throws Exception { SAXReader saxReader=new SAXReader(); File file=new File("d:\\test.xml"); try{ Document doc=saxReader.read(file); doc.accept(new Dom4jTest(). new MyVisitor()); }catch(DocumentException de){ de.printStackTrace(); } } }
运行结果:
节点:table
属性 : name = test
节点:rows
节点:row
节点: id = 1
节点: test = Test
节点:row
节点: id = 2
节点: test = Test2
可以看出把xml节点顺序的访问了一边。每个人可以根据不同的xml来实现自己的Visitor,不论怎么写都可以遍历出你所有的节点,这就是visitor的厉害之处。访问者模式也不是万能的,他的缺点是当数据结构变化时,他的visitor接口及其实现都要改变。所以访问者模式不能使用在经常变化的数据接口上。在Gof的设计模式中,有以下情形可以考虑使用设计模式:
1、一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。
3、当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
4、 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
这些个人看来都是建议,项目中还要具体问题具体分析了。
设计模式系列目录:
相关推荐
以上只是部分Java设计模式的介绍,实际的“DesignPatterns”压缩包可能包含了这些模式的源代码实现,通过阅读和学习这些代码,开发者可以更好地理解和运用设计模式,提升代码质量。同时,结合博主提供的博客链接,...
本资料“《java设计模式》课后习题模拟试题解答——刘伟.zip”主要涵盖了Java设计模式的学习与应用,特别是针对刘伟教授的相关课程的课后习题及模拟试题的解答。 设计模式分为三大类:创建型、结构型和行为型模式。...
通过深入学习《设计模式——Java语言中的应用》,开发者能够更好地理解和应用这些模式,从而编写出更加高效、可扩展的Java程序。无论是初级开发者还是经验丰富的程序员,都应该不断探索和实践设计模式,以提升自己的...
《Java Web开发实践教程——从设计到实现(第2版)》是一本深入探讨Java Web技术的权威指南,尤其适合初学者和有一定基础的开发者。本教程通过PPT的形式,以直观、易懂的方式讲解了从项目规划到实际开发的全过程。在...
《Java与模式——源码》这个主题涉及到的是Java编程语言中的设计模式应用,以及如何通过源代码来理解和学习这些模式。设计模式是软件工程中的一种最佳实践,它们是解决常见问题的经验总结,使得代码更易读、易维护、...
### Java设计模式经典教程知识点概览 #### 一、设计模式概述 设计模式是一种软件设计方法,它为软件开发者提供了一种标准化的方式去解决常见的软件设计问题。设计模式的使用可以提高代码的可读性和可维护性,同时...
目录: 前 言 第一部分 大旗不挥,谁敢冲锋——热身篇 第1章 单一职责原则 1.1 我是“牛”类,我可以担任多职吗 1.2 绝杀技,打破你的传统思维 1.3 我单纯,所以我快乐 1.4 最佳实践 ...附录:23个设计模式
总结来说,Java设计模式的学习是一条不断深化的过程,它从UML开始,到设计模式的具体分类和应用,再到软件设计原则的领悟,每一步都是提升个人技术能力的重要环节。通过不断的学习和实践,开发者能够成为一名更加...
根据提供的标题、描述以及部分内容,本文将深入探讨Java设计模式,并着重分析23种常见的设计模式,以帮助Java程序员更好地理解和应用这些模式。 ### Java设计模式概述 Java设计模式是面向对象软件设计的一种通用可...
《Java与模式》是闫宏大师的一部经典之作,它将古老的哲学智慧——道德经的智慧,巧妙地融入到现代编程语言Java的设计模式之中。这本书不仅深入浅出地讲解了23种经典的设计模式,还提供了丰富的实践案例,旨在帮助...
以下是关于JAVA设计模式中提及的四种模式——工厂模式、代理模式、迭代器模式以及责任链模式的详细说明。 1. **工厂模式**:工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。在工厂模式中,当创建...
通过程序范例和UML图示来一一解说,书中代码的重要部分加了标注以使读者更加容易理解,再加上图文并茂,对于初学者还是程序设计高手来说,这都是一本学习和认识JAVA设计模式的一本好书。(注意,本资源附带书中源...
在编程世界中,Java设计模式是面向对象编程领域的一个重要概念,它为解决常见的软件设计问题提供了可重用的解决方案。对于新手来说,设计模式可能显得深奥难懂,但它们实际上是经验丰富的开发者用来提高代码质量、可...
### JAVA设计模式总结之23种设计模式及六大原则 #### 一、设计模式之六大原则 ##### 总原则:开闭原则(Open Close Principle) 开闭原则是设计模式中最核心的原则之一,它强调的是软件实体(类、模块、函数等)...
### Java设计模式(1)——理解与应用 #### 一、设计模式概述 设计模式是在软件工程领域中,为了应对特定问题或情境而形成的最佳实践集合。这些模式可以帮助开发者更高效地解决常见问题,提高代码的可重用性和可...
8. **设计模式**:了解并能应用常见的设计模式,如单例、工厂、观察者、装饰器等,能提高代码的可维护性和复用性。 9. **垃圾回收与内存管理**:理解JVM的工作原理,包括内存区域划分、垃圾回收机制、性能调优等,...
### Java设计模式详解 #### 单例模式(Singleton Pattern)...通过上述介绍,我们了解了两种重要的设计模式——单例模式和观察者模式。这两种模式都有助于提高代码的可维护性和扩展性,是软件开发中不可或缺的一部分。
Java面试题是每个Java开发者在求职过程中必须面对的挑战,涵盖范围广泛,从基础概念到高级特性和设计模式。这份终极列表包含115个Java面试题和答案,旨在帮助求职者全面准备,以期在面试中表现出色。以下是部分核心...