• 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/

  • 相关阅读:
    CXF入门案例
    计算python内部数据结构时间效率-源代码
    笨办法学习python-ex41源码加自己注释
    python之random模块
    python之模块、类、对象
    购物车代码
    ql的python学习之路-day1
    数组转置(函数指针,回调函数)
    将一句话按单词逆转
    *一个二级指针的练习(输入一个数,输出对应的月份)
  • 原文地址:https://www.cnblogs.com/jiangbei/p/7777376.html
Copyright © 2020-2023  润新知