美文网首页
设计模式[23]-访问者模式-Visitor Pattern

设计模式[23]-访问者模式-Visitor Pattern

作者: 郭寻抚 | 来源:发表于2017-05-16 17:56 被阅读1080次

    1.访问者模式简介

    访问者模式(Visitor Pattern)模式是行为型(Behavioral)设计模式,提供一个作用于某种对象结构上的各元素的操作方式,可以使我们在不改变元素结构的前提下,定义作用于元素的新操作。

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

    访问者模式一共有五种角色:

    (1) Vistor(抽象访问者):为该对象结构中具体元素角色声明一个访问操作接口。

    (2) ConcreteVisitor(具体访问者):每个具体访问者都实现了Vistor中定义的操作。

    (3) Element(抽象元素):定义了一个accept操作,以Visitor作为参数。

    (4) ConcreteElement(具体元素):实现了Element中的accept()方法,调用Vistor的访问方法以便完成对一个元素的操作。

    (5) ObjectStructure(对象结构):可以是组合模式,也可以是集合;能够枚举它包含的元素;提供一个接口,允许Vistor访问它的元素。

    访问者模式类图如下:


    Visitor Pattern.png

    2. 访问者模式举例

    如果老师教学反馈得分大于等于85分、学生成绩大于等于90分,则可以入选成绩优秀奖;如果老师论文数目大于8、学生论文数目大于2,则可以入选科研优秀奖。

    在这个例子中,老师和学生就是Element,他们的数据结构稳定不变。从上面的描述中,我们发现,对数据结构的操作是多变的,一会儿评选成绩,一会儿评选科研,这样就适合使用访问者模式来分离数据结构和操作。

    序号 类名 角色 说明
    1 Visitor Visitor 抽象访问者
    2 GradeSelection ConcreteVisitor 具体访问者
    3 ResearcherSelection ConcreteVisitor 具体访问者
    4 Element Element 抽象元素
    5 Teacher ConcreteElement 具体元素
    6 Student ConcreteElement 具体元素
    7 ObjectStructure ObjectStructure 对象结构
    8 VisitorClient 客户端 演示调用

    举例的类图如下:


    Award Selection.png

    1. Visitor 抽象访问者

    /**
     * 抽象访问者,为该对象结构中具体元素角色声明一个访问操作接口。
     */
    public interface Visitor {
    
        void visit(Student element);
    
        void visit(Teacher element);
    
    }
    

    2. GradeSelection 选拔优秀成绩者

    /**
     * 具体访问者,实现了Vistor中定义的操作。
     */
    public class GradeSelection implements Visitor {
    
        private String awardWords = "[%s]的分数是%d,荣获了成绩优秀奖。";
    
        @Override
        public void visit(Student element) {
            // 如果学生考试成绩超过90,则入围成绩优秀奖。
            if (element.getGrade() >= 90) {
                System.out.println(String.format(awardWords, 
                        element.getName(), element.getGrade()));
            }
        }
    
        @Override
        public void visit(Teacher element) {
            // 如果老师反馈得分超过85,则入围成绩优秀奖。
            if (element.getScore() >= 85) {
                System.out.println(String.format(awardWords, 
                        element.getName(), element.getScore()));
            }
        }
    }
    

    3. ResearcherSelection,选拔优秀科研者

    /**
     * 具体访问者,实现了Vistor中定义的操作。
     */
    public class ResearcherSelection implements Visitor {
    
        private String awardWords = "[%s]的论文数是%d,荣获了科研优秀奖。";
    
        @Override
        public void visit(Student element) {
            // 如果学生发表论文数超过2,则入围科研优秀奖。
            if(element.getPaperCount() > 2){
                System.out.println(String.format(awardWords,
                        element.getName(),element.getPaperCount()));
            }
        }
    
        @Override
        public void visit(Teacher element) {
            // 如果老师发表论文数超过8,则入围科研优秀奖。
            if(element.getPaperCount() > 8){
                System.out.println(String.format(awardWords,
                        element.getName(),element.getPaperCount()));
            }
        }
    }
    

    4. Element,抽象元素角色

    /**
     * 抽象元素角色,定义了一个accept操作,以Visitor作为参数。
     */
    public interface Element {
    
        //接受一个抽象访问者访问
        void accept(Visitor visitor);
    
    }
    

    5.Teacher,具体元素

    /**
     * 具体元素,允许visitor访问本对象的数据结构。
     */
    public class Teacher implements Element {
    
        private String name; // 教师姓名
        private int score; // 评价分数
        private int paperCount; // 论文数
    
        // 构造器
        public Teacher(String name, int score, int paperCount) {
            this.name = name;
            this.score = score;
            this.paperCount = paperCount;
        }
    
        // visitor访问本对象的数据结构
        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getScore() {
            return score;
        }
    
        public void setScore(int score) {
            this.score = score;
        }
    
        public int getPaperCount() {
            return paperCount;
        }
    
        public void setPaperCount(int paperCount) {
            this.paperCount = paperCount;
        }
    }
    

    6. Student,具体元素

    /**
     * 具体元素,允许visitor访问本对象的数据结构。
     */
    public class Student implements Element {
    
        private String name; // 学生姓名
        private int grade; // 成绩
        private int paperCount; // 论文数
    
        // 构造器
        public Student(String name, int grade, int paperCount) {
            this.name = name;
            this.grade = grade;
            this.paperCount = paperCount;
        }
    
        // visitor访问本对象的数据结构
        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getGrade() {
            return grade;
        }
    
        public void setGrade(int grade) {
            this.grade = grade;
        }
    
        public int getPaperCount() {
            return paperCount;
        }
    
        public void setPaperCount(int paperCount) {
            this.paperCount = paperCount;
        }
    }
    

    7.ObjectStructure, 对象结构

    /**
     * 对象结构,是元素的集合,提供元素的访问入口。
     */
    public class ObjectStructure {
    
        // 使用集合保存Element元素,示例没有考虑多线程的问题。
        private ArrayList<Element> elements = new ArrayList<>();
    
        /**
         * 访问者访问元素的入口
         *
         * @param visitor 访问者
         */
        public void accept(Visitor visitor) {
            for (int i = 0; i < elements.size(); i++) {
                Element element = elements.get(i);
                element.accept(visitor);
            }
        }
    
        /**
         * 把元素加入到集合
         *
         * @param element 待添加的元素
         */
        public void addElement(Element element) {
            elements.add(element);
        }
    
        /**
         * 把元素从集合中移除
         *
         * @param element 要移除的元素
         */
        public void removeElement(Element element) {
            elements.remove(element);
        }
    }
    

    8.VisitorClient 客户端

    /**
     * 如果教师发表论文数超过8篇或者学生论文超过2篇可以评选科研优秀奖,
     * 如果教师教学反馈分大于等于85分或者学生成绩大于等于90分可以评选成绩优秀奖。
     */
    public class VisitorClient {
    
        public static void main(String[] args) {
            // 初始化元素
            Element stu1 = new Student("Student Jim", 92, 3);
            Element stu2 = new Student("Student Ana", 89, 1);
            Element t1 = new Teacher("Teacher Mike", 83, 10);
            Element t2 = new Teacher("Teacher Lee", 88, 7);
            // 初始化对象结构
            ObjectStructure objectStructure = new ObjectStructure();
            objectStructure.addElement(stu1);
            objectStructure.addElement(stu2);
            objectStructure.addElement(t1);
            objectStructure.addElement(t2);
            // 定义具体访问者,选拔成绩优秀者
            Visitor gradeSelection = new GradeSelection();
            // 具体的访问操作,打印输出访问结果
            objectStructure.accept(gradeSelection);
            System.out.println("----结构不变,操作易变----");
            // 数据结构是没有变化的,如果我们还想增加选拔科研优秀者的操作,那么如下。
            Visitor researcherSelection = new ResearcherSelection();
            objectStructure.accept(researcherSelection);
        }
    }
    

    结果输出

    [Student Jim]的分数是92,荣获了成绩优秀奖。
    [Teacher Lee]的分数是88,荣获了成绩优秀奖。
    ----结构不变,操作易变----
    [Student Jim]的论文数是3,荣获了科研优秀奖。
    [Teacher Mike]的论文数是10,荣获了科研优秀奖。
    

    3. 总结

    如果一个对象结构比较复杂,同时结构稳定不易变化,但却需要经常在此结构上定义新的操作,那就非常合适使用访问者模式,比如复杂的集合对象、XML文档解析、编译器的设计等。

    访问者模式使我们更加容易的增加访问操作,但增加元素比较困难,需要我们修改抽象访问类和所有的具体访问类。

    (完)

    相关文章

      网友评论

          本文标题:设计模式[23]-访问者模式-Visitor Pattern

          本文链接:https://www.haomeiwen.com/subject/zeiwtxtx.html