• 行为型设计模式


    基本介绍

    访问者模式(Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的情况下,定义作用于这些元素的新的操作。

    如果系统的数据结构是比较稳定的,但其操作(算法)是易于变化的,那么使用访问者模式是个不错的选择;如果数据结构是易于变化的,则不适合使用访问者模式。

    基本原理:在被访问的类里添加一个对外提供接待访问者的接口

    模式结构

    • Visitor(抽象访问者):声明访问者可以访问哪些元素。具体到代码中就是 visit 方法中接受哪些参数,就可以访问哪些元素。
    • ConcreteVisitor(具体访问者):实现了抽象访问者中定义的操作,决定访问者访问后做什么事,怎么做。
    • Element(抽象元素):定义了一个 accept 操作,以 Visitor 作为参数,声明接受哪类访问者访问。
    • ConcreteElement(具体元素):实现了 Element 中的 accept() 方法,调用 Visitor 的访问方法以便完成对一个元素的操作。
    • ObjectStructure(对象结构):可以是组合模式,也可以是集合;能够枚举它包含的元素;提供一个接口,允许 Vistor 访问它的元素。

    举例说明

    老师考核成绩大于等于 85 分或者学生考核成绩大于 90 分,可以获得成绩优秀奖;

    老师发表论文数大于等于 10 篇或者学生发表论文数大于等于 5 篇,可以获得科研优秀奖;

    上述例子中,学生和老师就是具体元素,因为他们的数据结构基本不变,但对数据结构的操作是多变的,一会评选成绩优秀奖,一会评选科研优秀奖,因此可以使用访问者模式解决。

    抽象访问者,可以访问学生和老师

    public interface Visitor {
        /**
         * 访问学生元素
         */
        void visit(Student student);
    
        /**
         * 访问老师元素
         */
        void visit(Teacher teacher);
    }
    

    具体访问者:评选成绩优秀奖

    public class ScoreJudge implements Visitor {
    
        private String awardWords = "%s的分数是%d,荣获了成绩优秀奖。";
    
        @Override
        public void visit(Student student) {
            if(student.getScore() >= 90){
                System.out.println(String.format(awardWords, student.getName(), student.getScore()));
            }
        }
    
        @Override
        public void visit(Teacher teacher) {
            if(teacher.getScore() >= 85){
                System.out.println(String.format(awardWords, teacher.getName(), teacher.getScore()));
            }
        }
    }
    

    具体访问者:评选科研优秀奖

    public class ResearchJudge implements Visitor {
        private String awardWords = "%s的论文数是%d,荣获了科研优秀奖。";
    
        @Override
        public void visit(Student student) {
            if(student.getPaperCount() >= 5){
                System.out.println(String.format(awardWords, student.getName(), student.getPaperCount()));
            }
        }
    
        @Override
        public void visit(Teacher teacher) {
            if(teacher.getPaperCount() >= 10){
                System.out.println(String.format(awardWords, teacher.getName(), teacher.getPaperCount()));
            }
        }
    }
    

    抽象元素,可以让 Visitor 访问

    public interface Element {
        /**
         * 接收一个抽象访问者访问
         */
        void accept(Visitor visitor);
    }
    

    具体元素:学生

    public class Student implements Element{
        private String name;
        private Integer score;
        private Integer paperCount;
    
        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
        //省略 getter、setter、全参构造方法
    }
    

    具体元素:老师

    public class Teacher implements Element {
        private String name;
        private Integer score;
        private Integer paperCount;
    
        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
        //省略 getter、setter、全参构造方法
    }
    

    对象结构

    public class ObjectStructure {
        /**
         * 用于存放所有元素
         */
        private List<Element> elements = new LinkedList<>();
    
        /**
         * 访问者访问元素的入口
         */
        public void accept(Visitor visitor) {
            for (Element element : elements) {
                element.accept(visitor);
            }
        }
    
        public void attach(Element e) {
            elements.add(e);
        }
    
        public void detach(Element e) {
            elements.remove(e);
        }
    }
    

    测试类

    public class Client {
        @Test
        public void test() {
            ObjectStructure objectStructure = new ObjectStructure();
            objectStructure.attach(new Student("Jack(student)", 95, 4));
            objectStructure.attach(new Student("Maria(student)", 85, 6));
            objectStructure.attach(new Teacher("Mike(teacher)", 80, 9));
            objectStructure.attach(new Teacher("Anna(teacher)", 85, 10));
            objectStructure.accept(new ScoreJudge());
            System.out.println("------------------------");
            objectStructure.accept(new ResearchJudge());
        }
    }
    

    运行结果

    Jack(student)的分数是95,荣获了成绩优秀奖。
    Anna(teacher)的分数是85,荣获了成绩优秀奖。
    ---------------------
    Maria(student)的论文数是6,荣获了科研优秀奖。
    Anna(teacher)的论文数是10,荣获了科研优秀奖。
    

    模式分析

    优点:

    • 符合单一职责原则,让程序具有优秀的扩展性,灵活性非常高
    • 可以对功能进行统一,可以做报表、UI、拦截器、过滤器等,适用于数据结构相对稳定的系统

    缺点:

    • 违背了迪米特法则,具体访问者关注了具体元素的内部细节,这样造成具体元素的变更比较困难
    • 违背了依赖倒转原则,访问者依赖的是具体元素,而不是抽象元素

    适用场景:

    • 一个对象结构比较复杂,同时结构稳定不易变化,但却需要经常在此结构上定义新的操作
    • 一个系统有稳定的数据结构,又有经常变化的功能需求,使用访问者模式是比较合适的
  • 相关阅读:
    左孩子右兄弟的字典树
    UVA 1401 Remember the Word
    HDOJ 4770 Lights Against Dudely
    UvaLA 3938 "Ray, Pass me the dishes!"
    UVA
    Codeforces 215A A.Sereja and Coat Rack
    Codeforces 215B B.Sereja and Suffixes
    HDU 4788 Hard Disk Drive
    HDU 2095 find your present (2)
    图的连通性问题—学习笔记
  • 原文地址:https://www.cnblogs.com/songjilong/p/12784568.html
Copyright © 2020-2023  润新知