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

2017-05-16  本文已影响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文档解析、编译器的设计等。

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

(完)

上一篇下一篇

猜你喜欢

热点阅读