设计模式解密(22)- 访问者模式
前言:访问者模式拆分
访问者模式基础篇 :http://www.cnblogs.com/JsonShare/p/7380772.html
访问者模式扩展篇 - 分派的概念: http://www.cnblogs.com/JsonShare/p/7381705.html
1、简介
定义:表示一个作用于其对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。
主要解决:稳定的数据结构和易变的操作耦合问题。就是把数据结构和作用于结构上的操作解耦合,使得操作集合可相对自由地演化。
本质:预留通路,回调实现。它的实现主要就是通过预先定义好调用的通路,在被访问的对象上定义accept方法,在访问者的对象上定义visit方法;然后在调用真正发生的时候,通过两次分发的技术,利用预先定义好的通路,回调到访问者具体的实现上。
英文:Visitor
类型:行为型
2、类图及组成
(引)类图:
组成:
Visitor抽象访问者接口:它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素个数(Element的实现类个数)是一样的,从这点不难看出,访问者模式要求元素类的个数不能改变(不能改变的意思是说,如果元素类的个数经常改变,则说明不适合使用访问者模式)。
ConcreteVisitor具体访问者角色:它需要给出对每一个元素类访问时所产生的具体行为。
Element抽象节点(元素)角色:它定义了一个接受访问者(accept)的方法,其意义是指,每一个元素都要可以被访问者访问。
ConcreteElement具体节点(元素)角色:它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
ObjectStructure结构对象角色:这个便是定义当中所提到的对象结构,对象结构是一个抽象表述,具体点可以理解为一个具有容器性质或者复合对象特性的类,它会含有一组元素(Element),并且可以迭代这些元素,供访问者访问。
代码结构:
/** * 抽象访问者角色:为每一个具体节点都准备了一个访问操作。 * 这里由于有两个节点,因此,对应就有两个访问操作。 */ public interface Visitor { /** * 对应于NodeA的访问操作 */ public void visit(NodeA node); /** * 对应于NodeB的访问操作 */ public void visit(NodeB node); } /** * 具体访问者VisitorA类 */ public class VisitorA implements Visitor { /** * 对应于NodeA的访问操作 */ @Override public void visit(NodeA node) { System.out.println(node.operationA()); } /** * 对应于NodeB的访问操作 */ @Override public void visit(NodeB node) { System.out.println(node.operationB()); } } /** * 具体访问者VisitorB类 */ public class VisitorB implements Visitor { /** * 对应于NodeA的访问操作 */ @Override public void visit(NodeA node) { System.out.println(node.operationA()); } /** * 对应于NodeB的访问操作 */ @Override public void visit(NodeB node) { System.out.println(node.operationB()); } } /** * 抽象节点类 */ public abstract class Node { /** * 接受操作 */ public abstract void accept(Visitor visitor); } /** * 具体节点类NodeA */ public class NodeA extends Node { /** * 接受操作 */ @Override public void accept(Visitor visitor) { visitor.visit(this); } /** * NodeA特有的方法 */ public String operationA() { return "NodeA"; } } /** * 具体节点类NodeB */ public class NodeB extends Node { /** * 接受方法 */ @Override public void accept(Visitor visitor) { visitor.visit(this); } /** * NodeB特有的方法 */ public String operationB() { return "NodeB"; } } /** * 结构对象角色类 * 这个结构对象角色持有一个聚集,并向外界提供add()方法作为对聚集的管理操作。通过调用这个方法,可以动态地增加一个新的节点。 */ public class ObjectStructure { private List<Node> nodes = new ArrayList<Node>(); /** * 执行方法操作 */ public void action(Visitor visitor) { for (Node node : nodes) { node.accept(visitor); } } /** * 添加一个新元素 */ public void add(Node node) { nodes.add(node); } } /** * 客户端类 */ public class Client { public static void main(String[] args) { //创建一个结构对象 ObjectStructure os = new ObjectStructure(); //给结构增加一个节点 os.add(new NodeA()); //给结构增加一个节点 os.add(new NodeB()); //创建一个访问者 Visitor visitor = new VisitorA(); os.action(visitor); } }
3、实例引入
场景:很多人都有养宠物的习惯,这里就以此为例
访问者角色:可以给宠物喂食的人
具体访问者角色:主人、其他人
抽象元素角色:动物抽象类
具体元素角色:宠物狗、宠物猫
package com.designpattern.Visitor; /** * 抽象访问者接口 -- 人 * @author Json<<json1990@foxmail.com>> */ public abstract class Person { /** * 喂食狗 */ public abstract void feed(Cat cat); /** * 喂食猫 */ public abstract void feed(Dog dog); }
package com.designpattern.Visitor; /** * 具体访问者角色 -- 主人 * @author Json<<json1990@foxmail.com>> */ public class Owner extends Person { @Override public void feed(Cat cat) { System.out.println("主人喂食猫"); } @Override public void feed(Dog dog) { System.out.println("主人喂食狗"); } }
package com.designpattern.Visitor; /** * 具体访问者角色 -- 其他人 * @author Json<<json1990@foxmail.com>> */ public class Someone extends Person { @Override public void feed(Cat cat) { System.out.println("其他人喂食猫"); } @Override public void feed(Dog dog) { System.out.println("其他人喂食狗"); } }
package com.designpattern.Visitor; /** * 抽象节点(元素)角色 -- 宠物 * @author Json<<json1990@foxmail.com>> */ public abstract class Animal { //吃食操作 public abstract void accept(Person person); }
package com.designpattern.Visitor; /** * 具体节点(元素)角色 -- 宠物狗 * @author Json<<json1990@foxmail.com>> */ public class Dog extends Animal { @Override public void accept(Person person) { person.feed(this); System.out.println("好好吃,汪汪汪!!!"); } }
package com.designpattern.Visitor; /** * 具体节点(元素)角色 -- 宠物猫 * @author Json<<json1990@foxmail.com>> */ public class Cat extends Animal { @Override public void accept(Person person) { person.feed(this); System.out.println("好好吃,喵喵喵!!!"); } }
package com.designpattern.Visitor; import java.util.ArrayList; import java.util.List; /** * 结构对象角色类 -- 主人家 * @author Json<<json1990@foxmail.com>> */ public class Home { private List<Animal> nodeList = new ArrayList<>(); public void action(Person person) { for (Animal node : nodeList) { node.accept(person); } } /** * 添加操作 * @param animal */ public void add(Animal animal) { nodeList.add(animal); } }
测试:
package com.designpattern.Visitor; /** * 客户端测试 * @author Json<<json1990@foxmail.com>> */ public class Client { public static void main(String[] args) { Home owerHome = new Home(); owerHome.add(new Dog()); owerHome.add(new Cat()); Owner owner = new Owner(); owerHome.action(owner); Someone someone = new Someone(); owerHome.action(someone); } }
结果:
主人喂食狗
好好吃,汪汪汪!!!
主人喂食猫
好好吃,喵喵喵!!!
其他人喂食狗
好好吃,汪汪汪!!!
其他人喂食猫
好好吃,喵喵喵!!!
4、优缺点
优点:
扩展性好:能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
复用性好:可以通过访问者来定义整个对象结构通用的功能,从而提高复用程度。
分离无关行为:可以通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问者的功能都比较单一。
缺点:
对象结构变化很困难:不适用于对象结构中的类经常变化的情况,因为对象结构发生了改变,访问者的接口和访问者的实现都要发生相应的改变,代价太高。
破坏封装:访问者模式通常需要对象结构开放内部数据给访问者和ObjectStructrue,这破坏了对象的封装性。
5、使用场景
1、数据结构稳定,作用于数据结构的操作经常变化的时候。
2、当一个数据结构中,一些元素类需要负责与其不相关的操作的时候,为了将这些操作分离出去,以减少这些元素类的职责时,可以使用访问者模式。
3、有时在对数据结构上的元素进行操作的时候,需要区分具体的类型,这时使用访问者模式可以针对不同的类型,在访问者类中定义不同的操作,从而去除掉类型判断。
6、总结
访问者模式把数据结构和作用于结构上的操作解耦合,使得操作集合可相对自由地演化。访问者模式适用于数据结构相对稳定算法又易变化的系统。因为访问者模式使得算法操作增加变得容易。若系统数据结构对象易于变化,经常有新的数据对象增加进来,则不适合使用访问者模式。
PS:源码地址 https://github.com/JsonShare/DesignPattern/tree/master
PS:原文地址 http://www.cnblogs.com/JsonShare/p/7380772.html