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/

posted @ 2017-11-03 11:34  ---江北  阅读(221)  评论(0编辑  收藏  举报
TOP