设计模式学习(三)——访问者模式

访问者模式定义及优缺点

访问者模式(Visitor Pattern)用于表示一个作用于某对象结构中的各元素的操作,它使得用户可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

访问者模式使得对对象的操作变得更为灵活,它将数据结构和作用于结构上的操作之间的耦合解脱开,使得增加一个新的访问者类变得很方便。

访问者模式的优点:

  • 允许你对组合结构加入新的操作,而无需改变结构本身;
  • 想要加入新的操作,相对容易;

  • 访问者所进行的操作,其代码是集中在一起的。

访问者模式的缺点:

  • 当采用访问者模式时,会打破组合类的封装;

  • 因为游走的功能牵涉其中,所以对组合结构的改变就更加困难。


访问者模式结构图

图中:

  • Visitor表示抽象访问者,它为对象结构类中每一个ConcreteElement的类声明一个Visit操作,通过这个操作的名称或方法签名(方法的参数和返回类型)可识别传出Visit请求给访问者的类,这就使得访问者可以确定正要被访问的元素的具体类,访问者就可以直接通过该元素的特定接口(Element)访问到它;

  • ConcreteVisitor表示具体访问者,它实现了每个由抽象访问者声明的操作,每一个操作用于访问对象结构中一种类型的元素;

  • Element表示抽象元素,在其中声明一个accept()操作,它以一个抽象访问者作为参数;

  • ConcreteElement表示具体元素,它实现了accept()操作,在accept()中调用访问者的访问方法以便完成对一个元素的操作;

  • ObjectStructure表示对象结构,它提供一个高层的接口以允许访问者访问它的元素,可以是一个组合模式或是一个集合,如一个列表或一个无序集合,可结合迭代器模式枚举其中的元素。


访问者模式实例——奖励审批

实例说明

某高校奖励审批系统可以实现教师奖励和学生奖励的审批(AwardCheck),如果教师发表论文数超过10篇或者学生论文超过2篇可以评选科研奖,如果教师教学反馈分大于等于90分或者学生平均成绩大于等于90分可以评选成绩优秀奖,使用访问者模式设计该系统,以判断候选人集合中的教师或学生是否符合某种获奖要求。

实例类图

实例类图中与访问者模式结构图对应的类主要有:

  • AwardCheck充当抽象访问者角色;

  • ScientificAwardCheck和ExcellenceAwardCheck充当具体访问者角色;

  • Person充当抽象元素角色;

  • Teacher和Student充当具体元素角色;

  • CandidateList充当对象结构角色。

实例代码

AwardCheck.java:

//抽象奖励审批类:抽象访问者类
abstract class AwardCheck
{
public abstract void visit(Teacher teacher);
public abstract void visit(Student student);
}

ScientificAwardCheck.java:

//科研奖审批类:具体访问者类
class ScientificAwardCheck extends AwardCheck
{

public void visit(Teacher teacher)
{
if(teacher.getPaperAmount()>=10)
{
System.out.println(teacher.getName() + "可评选教师科研奖!");
}
}
public void visit(Student student)
{
if(student.getPaperAmount()>=2)
{
System.out.println(student.getName() + "可评选学生科研奖!");
}
}
}

ExcellenceAwardCheck.java:

//成绩优秀奖审批类:具体访问者类
class ExcellenceAwardCheck extends AwardCheck
{

public void visit(Teacher teacher)
{
if(teacher.getFeedbackScore()>=90)
{
System.out.println(teacher.getName() + "可评选教师成绩优秀奖!");
}
}
public void visit(Student student)
{
if(student.getScore()>=90)
{
System.out.println(student.getName() + "可评选学生成绩优秀奖!");
}
}
}

Person.java:

//申请人类:抽象元素类
interface Person
{
public void accept(AwardCheck check);
}

Teacher.java:

//教师类:具体元素类
class Teacher implements Person
{

private String name;
private int paperAmount;
private double feedbackScore;

public void setName(String name)
{

this.name = name;
}

public void setPaperAmount(int paperAmount)
{

this.paperAmount = paperAmount;
}

public void setFeedbackScore(double feedbackScore)
{

this.feedbackScore = feedbackScore;
}

public String getName()
{

return (this.name);
}

public int getPaperAmount()
{

return (this.paperAmount);
}

public double getFeedbackScore()
{

return (this.feedbackScore);
}

public void accept(AwardCheck check)
{

check.visit(this);
}
}

Student.java:

//学生类:具体元素类
class Student implements Person
{

private String name;
private int paperAmount;
private double score;

public void setName(String name)
{

this.name = name;
}

public void setPaperAmount(int paperAmount)
{

this.paperAmount = paperAmount;
}

public void setScore(double score)
{

this.score = score;
}

public String getName()
{

return (this.name);
}

public int getPaperAmount()
{

return (this.paperAmount);
}

public double getScore()
{

return (this.score);
}

public void accept(AwardCheck check)
{

check.visit(this);
}
}

CandidateList.java:

import java.util.*;

//候选人集合类:对象结构
class CandidateList
{
private ArrayList<Person> list = new ArrayList<Person>();
public void addPerson(Person person)
{

list.add(person);
}
public void removePerson(Person person)
{

list.remove(person);
}
public void accept(AwardCheck check)
{

Iterator i = list.iterator();
while(i.hasNext())
{
((Person)i.next()).accept(check);
}
}
}

Client.java:

//客户端测试类
class Client
{
public static void main(String args[])
{

CandidateList list = new CandidateList();
AwardCheck sac,eac;
Teacher teacher = new Teacher();
Student student = new Student();
teacher.setName("李雷");
teacher.setPaperAmount(15);
teacher.setFeedbackScore(92);
student.setName("韩梅梅");
student.setPaperAmount(2);
student.setScore(85);
list.addPerson(teacher);
list.addPerson(student);
sac = new ScientificAwardCheck();
list.accept(sac);
System.out.println("-------------------------");
eac = new ExcellenceAwardCheck();
list.accept(eac);
}
}

运行结果

附加描述

在本实例中,CandidateList类中定义了一个ArrayList类型的集合对象,用于存储待审核的学生和教师信息,在其accept()方法中通过参数传入一个访问者对象,该访问者对象将遍历审核存储在集合中的学生对象和教师对象,取出存储在元素对象中的论文数量和平均成绩,判断是否符合科研奖和成绩优秀奖的评选条件,再输出相应的判断结果。


后话

看书学习这个设计模式时,有的话看了一遍两遍,根本就不明白是要表达什么意思,整段整段的专用术语晦涩难懂。可是再坚持看几遍,就会恍然大悟,如梦初醒。这真是验证了那句名言——“书读百遍,其义自现。”