zoukankan      html  css  js  c++  java
  • 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/

  • 相关阅读:
    Hadoop源码分析1: 客户端提交JOB
    《分布式系统原理与范型》习题答案 6.一致性和复制
    《分布式系统原理与范型》习题答案 5.同步
    《分布式系统原理与范型》习题答案 4.命名
    《分布式系统原理与范型》习题答案 3.进程
    《分布式系统原理与范型》习题答案 2.通信
    《分布式系统原理与范型》习题答案 1.绪论
    计算机基础知识面试
    机器学习面试题
    计算机网络面试题
  • 原文地址:https://www.cnblogs.com/jiangbei/p/7777376.html
Copyright © 2011-2022 走看看