• 行为模式之访问者模式


    访问者模式(Visitor Pattern)的目的是封装一些于某种数据结构元素之上的操作,一旦这些元素需要修改,接受这个操作的数据结构则可以保持不变。

    定义:

    • 封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义于作用这些元素的新的操作。

    访问者模式的类图如下。

    访问者模式涉及以下5个角色。

    • 抽象访问者(Visitor)角色:该角色声明一个或多个访问操作,定义访问者可以访问哪些元素。
    • 具体访问者(Concrete Visitor)角色:该角色实现抽象访问者角色中的各个访问操作。
    • 抽象元素(Element)角色:声明一个接受操作,接受一个访问者对象。
    • 具体元素(Concrete Element)角色:实现抽象元素中的接受操作。
    • 结构对象(Object Structure)角色:该角色有以下责任,可以遍历结构中的所有元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素,也可以设计一个复合对象或者一个集合,如List或Set.

    Element.java

    // 抽象元素角色
    public abstract class Element {
        // 接受操作
        public abstract void accept(Visitor vi);
    }

    ConcreteElement1.java

    // 具体元素1
    public class ConcreteElement1 extends Element {
        @Override
        public void accept(Visitor vi) {
            vi.visit(this);
        }
        // 业务逻辑方法
        public void operation() {
            System.out.println("访问元素1");
        }
    }

    ConcreteElement2.java

    //具体元素2
    public class ConcreteElement2 extends Element {
        @Override
        public void accept(Visitor vi) {
            vi.visit(this);
        }
        // 业务逻辑方法
        public void operation() {
            System.out.println("访问元素2");
        }
    }

    Visitor.java

    // 抽象访问者
    public interface Visitor {
        // 可以访问哪些对象
        public void visit(ConcreteElement1 el1);
        public void visit(ConcreteElement2 el2);
    }

    ConcreteVisitor.java

    // 具体访问者角色
    public class ConcreteVisitor implements Visitor {
        // 访问元素1
        @Override
        public void visit(ConcreteElement1 el1) {
            el1.operation();
        }
        // 访问元素2
        @Override
        public void visit(ConcreteElement2 el2) {
            el2.operation();
        }
    }

    ObjectStructure.java

    // 结构对象角色
    public class ObjectStructure {
        private Vector<Element> elements;
        // 构造函数
        public ObjectStructure() {
            this.elements = new Vector<Element>(); 
        }
        // 执行访问操作
        public void action(Visitor vi) {
            for (Element e : elements) {
                e.accept(vi);
            }
        }
        // 添加新元素
        public void add(Element e) {
            elements.add(e);
        }
        // 元素生成器,这里通过一个工厂方法进行模拟
        public void createElements() {
            Random rand = new Random();
            for (int i = 0; i < 10; i++) {
                if (rand.nextInt(100) > 50) {
                    // 添加元素1
                    this.add(new ConcreteElement1());
                } else {
                    // 添加元素2
                    this.add(new ConcreteElement2());
                }
            }
        }
        
    }

    Client.java

    public class Client {
        public static void main(String[] args) {
            // 创建一个结构对象
            ObjectStructure os = new ObjectStructure();
            // 生成元素
            os.createElements();
            // 创建一个访问者对象
            Visitor vi = new ConcreteVisitor();
            // 访问者对结构进行访问(执行访问)
            os.action(vi);
        }
    }

    运行结果如下所示。

    访问元素1
    访问元素1
    访问元素1
    访问元素2
    访问元素2
    访问元素2
    访问元素1
    访问元素1
    访问元素1
    访问元素2

     优点:

    • 元素(Element)角色负责数据加载,Visitor负责数据展现。
    • 增加新的操作,直接在Visitor中增加一个方法。
    • 集中行为,系统容易维护。

    缺点:

    • 具体元素对访问者公布细节,破坏封装性;
    • 具体元素变更变更变得困难。具体元素的增加和修改等,需要具体访问者修改。
    • 违背了依赖倒置原则。访问者依赖的是具体元素,而不是抽象元素,抛弃了对接口的依赖,而直接依赖实现类,扩展比较困难。

    应用场景:

    • 一个对象结构包含很多类对象,它们有不同的接口。
    • 需要对一个对象结构中的对象进行很多不同并且不相关的操作,避免操作污染类。
    • 业务规则要求遍历多个不同的对象,这本身也是访问模式的出发点,迭代器模式只能访问同类或同接口的数据,而访问者模式是对迭代器模式的扩充,可以遍历不同的对象,执行不同的操作。

    摘自:

    青岛东合信息技术有限公司 . 设计模式(Java版) .  电子工业出版社,2012,161-166.

  • 相关阅读:
    LeetCode 501. 二叉搜索树中的众数
    LeetCode 404.左叶子之和
    LeetCode 450. 删除二叉搜索树中的节点
    LeetCode 437. 路径总和 III
    LeetCode 429. N 叉树的层序遍历
    LeetCode 213. 打家劫舍 II
    LeetCode 337. 打家劫舍 III
    LeetCode 198. 打家劫舍
    LeetCode 235. 二叉搜索树的最近公共祖先
    LeetCode 230. 二叉搜索树中第K小的元素
  • 原文地址:https://www.cnblogs.com/yewen1234/p/10059035.html
Copyright © 2020-2023  润新知