访问者模式

Visitor Pattern 对象行为型模式

意图

表示一个作用于某对象结构中的各元素的操作。它不允许在不改变各元素的类的前提下定义作用于这些元素的新操作

结构

访问者模式

其中:

  • Visitor (访问者) 为该对象结构中 ConcreteElement 的每一个类声明一个 Visit 操作。该操作的名字和特殊标识了发送 Visit 请求给该访问者的那个类,这使得访问者可以确定正被访问元素的具体的类。这样访问者就可以通过该元素的特定接口直接访问它
  • ConcreteVisitor (具体访问者) 实现每个有 Visitor 声明的操作,每个操作实现本算法的一部分,而该算法片段乃是对应于结构中对象的类。ConcreteVisitor 为该算法提供了上下文并存储它的局部状态。这一状态常常在遍历该结构的过程中累积结构
  • Element (元素) 定义以一个访问者为参数的 Accept 操作
  • ConcreteElement (具体元素) 实现以一个访问者为参数的 Accept 操作
  • ObjectStructure (对象结构) 能枚举它的元素;可以提供一个高层的接口以允许该访问者访问它的元素;可以是一个组合或者一个集合,如一个列表或一个无序集合

适用性

Visitor 模式适用于:

  • 一个对象结构包含很多类对象,它们有不同的接口,而用户想对这些对象实施一些依赖于其具体类的操作
  • 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而又想要避免这些操作 “污染” 这些对象的类。Visitor 使得用户可以将相关的操作集中起来定义一个类中。当该对象结构被很多应用共享时,用 Visitor 模式让每个应用仅包含需要用到的操作
  • 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作比较好

例子 1

某图书管理系统中管理着两种类型的文献:图书和论文。现在要求统计所有馆藏文献的总页码,采用 Visitor (访问者) 模式实现该要求,类图如下:

访问者模式-例子1

interface LibraryVisitor{
    void visit(Book p_book);
    void visit(Article p_article);
    void printSum();
}

class LibrarySumPrintVisitor implements LibraryVisitor{ // 打印总页数
    private int sum = 0;

    @Override
    public void visit(Book p_book) {
        sum = sum + p_book.getNumberOfPages();
    }

    @Override
    public void visit(Article p_article) {
        sum = sum + p_article.getNumberOfPages();
    }

    @Override
    public void printSum() {
        System.out.println("SUM = " + sum);
    }
}

interface LibraryItemInterface{
    void accept(LibraryVisitor visitor);
}

class Article implements LibraryItemInterface{
    private String m_title; // 论文名
    private String m_author; // 论文作者
    private int m_start_page;
    private int m_end_page;
    
    public Article(String p_author, String p_title, int p_start_page, int p_end_page){
        m_title = p_title;
        m_author = p_author;
        m_start_page = p_start_page;
        m_end_page = p_end_page;
    }
    
    public int getNumberOfPages(){
        return m_end_page - m_start_page;
    }

    @Override
    public void accept(LibraryVisitor visitor) {
        visitor.visit(this);
    }
}

class Book implements LibraryItemInterface{
    private String m_title; // 书名
    private String m_author; // 书作者
    private int m_pages; // 页数

    public Book(String p_author, String p_title, int p_pages){
        m_title = p_title;
        m_author = p_author;
        m_pages = p_pages;
    }

    public int getNumberOfPages(){
        return m_pages;
    }

    @Override
    public void accept(LibraryVisitor visitor) {
        visitor.visit(this);
    }
}

例子 2

import java.util.ArrayList;
import java.util.List;

public class VisitorPattern {
    public static void main(String[] args) {
        PersonStructure personStructure = new PersonStructure();

        Visitor1 visitor1 = new Visitor1();
        System.out.println("For Visitor1");
        personStructure.Accept(visitor1);
        System.out.println("The sum of student age: " + visitor1.getStudentAgeSum());
        System.out.println("The sum of teacher age: " + visitor1.getTeacherAgeSum());

        Visitor2 visitor2 = new Visitor2();
        System.out.println("For Visitor2");
        personStructure.Accept(visitor2);
        System.out.println("Max score: " + visitor2.getMaxScore());
        System.out.println("Max work year: " + visitor2.getMaxWorkYear());

    }
}

interface Visitor{
    public void VisitS(Student student);
    public void VisitT(Teacher teacher);
}

class Visitor1 implements Visitor{// 分别统计学生和老师的年龄总和
    private int studentAgeSum = 0;
    private int teacherAgeSum = 0;

    public int getStudentAgeSum() {
        return studentAgeSum;
    }

    public int getTeacherAgeSum() {
        return teacherAgeSum;
    }

    @Override
    public void VisitS(Student student){
        System.out.println("Visitor1: " + student.getName() + " Age: " + student.getAge());
        studentAgeSum += student.getAge();
    }
    public void VisitT(Teacher teacher){
        System.out.println("Visitor1: " + teacher.getName() + " Age: " + teacher.getAge());
        teacherAgeSum += teacher.getAge();
    }
}

class Visitor2 implements Visitor{ // 分别求出学生最高成绩以及老师最高工龄
    private int maxScore = -1;
    private int maxWorkYear = -1;

    public int getMaxScore() {
        return maxScore;
    }

    public int getMaxWorkYear() {
        return maxWorkYear;
    }

    @Override
    public void VisitS(Student student){
        System.out.println("Visitor2: " + student.getName() + " Score: " + student.getScore());
        if(student.getScore() > maxScore) maxScore = student.getScore();
        // maxScore = Math.max(maxScore, student.getScore());
    }
    public void VisitT(Teacher teacher){
        System.out.println("Visitor2: " + teacher.getName() + " WorkYear: " + teacher.getWorkYear());
        if(teacher.getWorkYear() > maxWorkYear) maxWorkYear = teacher.getWorkYear();
        // maxWorkYear = Math.max(maxWorkYear, teacher.getWorkYear());
    }
}

class PersonStructure{
    private List<Person> personList = new ArrayList<>();

    public PersonStructure(){
        personList.add(new Student("Mike", 16, 99));
        personList.add(new Student("Jane", 15, 100));

        personList.add(new Teacher("Alice mana", 20, 1));
    }
    public void Accept(Visitor visitor){
        for(Person p : personList){
            p.Accept(visitor);
        }
    }
}

abstract class Person{
    private String name;
    private int age;

    public abstract void Accept(Visitor visitor);

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

class Student extends Person{
    private int score;

    public int getScore() {
        return score;
    }

    public Student(String name, int age, int score){
        this.setName(name);
        this.setAge(age);
        this.score = score;
    }
    @Override
    public void Accept(Visitor visitor){
        visitor.VisitS(this);
    }
}

class Teacher extends Person{
    private int workYear;

    public int getWorkYear() {
        return workYear;
    }

    public Teacher(String name, int age, int workYear){
        this.setName(name);
        this.setAge(age);
        this.workYear = workYear;
    }
    @Override
    public void Accept(Visitor visitor){
        visitor.VisitT(this);
    }
}