zoukankan      html  css  js  c++  java
  • 行为型设计模式

    基本介绍

    访问者模式(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、拦截器、过滤器等,适用于数据结构相对稳定的系统

    缺点:

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

    适用场景:

    • 一个对象结构比较复杂,同时结构稳定不易变化,但却需要经常在此结构上定义新的操作
    • 一个系统有稳定的数据结构,又有经常变化的功能需求,使用访问者模式是比较合适的
  • 相关阅读:
    chown更改文件和目录的所有者
    常量指针和指向常量的指针
    声明,有人破解我的mmsplayer,声称开源的mms
    声明,有人破解我的mmsplayer,声称开源的mms
    慎用mysql的utf8unicode
    慎用mysql的utf8unicode
    在 Ubuntu 下使用 Android NDK r4b 编译 FFmpeg 0.6.3
    skia 生成 vs2008工程
    mmsPlayer 2.0 将开源java部分
    skia 生成 vs2008工程
  • 原文地址:https://www.cnblogs.com/songjilong/p/12784568.html
Copyright © 2011-2022 走看看