一、访问者模式概述
通过一个例子来引入访问者模式。
在医生开具处方单(药单)后,划价人员拿到处方单之后根据药品名称和数量计算总价,而药房工作人员根据药品名称和数量在不药品。
在这里,处方单看出一个药品信息的集合,里面包含了一种或多种不同类型的药品信息,不同类型的工作人员在操作同一个药品信息集合时将提供不同的处理方式,而且还可能会增加新类型的工作人员来操作处方单。
类似这种常见,可以使用访问者模式。
定义:
访问者模式:表示一个作用于某对象结构中的各个元素的操作。访问者模式让用户可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
二、访问者模式结构和实现
2.1 访问者模式的结构
访问者模式的结构较为复杂,包含以下5个角色:
- Visitor(抽象访问者):为对象结构中的每一个具体元素类声明类一个访问操作;
- ConcreteVisitor(具体访问者):实现了每个由抽象访问者声明的操作;
3.** Element(抽象元素)**:声明了一个accept()方法,用于接受访问者的访问操作; - ConcreteElement(具体元素):实现了accept()方法,在accept()方法中调用访问者的访问方法以便完成对一个元素的操作;
- ObjectStructure(对象结构):是一个元素的集合,它用于存放元素对象,并且提供了遍历其内部元素的方法。
2.2 访问者模式的实现
下面通过一个案例来进一步学习访问者模式。
//抽象访问者
/**
* 奖励类
* 抽象访问者类
*/
public abstract class Award {
public abstract void visit(Student candidate);
public abstract void visit(Teacher candidate);
}
//具体访问者
/**
* 科研奖
* 具体访问者
*/
public class ScientificAward extends Award {
@Override
public void visit(Student candidate) {
if (candidate.getThesisNumber() > 2) {
System.out.println("学生候选人:" + candidate.getName() + ",论文数"
+ candidate.getThesisNumber() + ",符合评审科研奖的要求");
}
}
@Override
public void visit(Teacher candidate) {
if (candidate.getThesisNumber() > 10) {
System.out.println("教师候选人:" + candidate.getName() + ",论文数"
+ candidate.getThesisNumber() + ",符合评审科研奖的要求");
}
}
}
/**
* 成绩优秀奖
* 具体访问者
*/
public class AchievementAward extends Award {
@Override
public void visit(Student candidate) {
if (candidate.getScore() >= 90) {
System.out.println("学生候选人:" + candidate.getName() + ",平均成绩"
+ candidate.getScore() + ",符合评审成绩优秀奖的要求");
}
}
@Override
public void visit(Teacher candidate) {
if (candidate.getScore() >= 90) {
System.out.println("教师候选人:" + candidate.getName() + ",教学反馈分"
+ candidate.getScore() + ",符合评审成绩优秀奖的要求");
}
}
}
//抽象元素
/**
* 候选人
* 抽象元素类
*/
public interface Candidate {
void accept(Award handle);//接受一个抽象访问者访问
}
//具体元素
/**
* 学生
* 具体元素类
*/
public class Student implements Candidate{
//姓名
private String name;
//成绩分数
private Double score;
//论文数量
private Integer thesisNumber;
public Student(String name, Double score, Integer thesisNumber) {
this.name = name;
this.score = score;
this.thesisNumber = thesisNumber;
}
@Override
public void accept(Award handle) {
handle.visit(this);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getScore() {
return score;
}
public void setScore(Double score) {
this.score = score;
}
public Integer getThesisNumber() {
return thesisNumber;
}
public void setThesisNumber(Integer thesisNumber) {
this.thesisNumber = thesisNumber;
}
}
/**
* 教师
* 具体元素类
*/
public class Teacher implements Candidate{
//姓名
private String name;
//成绩分数
private Double score;
//论文数量
private Integer thesisNumber;
public Teacher(String name, Double score, Integer thesisNumber) {
this.name = name;
this.score = score;
this.thesisNumber = thesisNumber;
}
@Override
public void accept(Award handle) {
handle.visit(this);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getScore() {
return score;
}
public void setScore(Double score) {
this.score = score;
}
public Integer getThesisNumber() {
return thesisNumber;
}
public void setThesisNumber(Integer thesisNumber) {
this.thesisNumber = thesisNumber;
}
}
//对象结构
/**
* 候选人列表类
* 充当对象结构
*
*/
public class CandidateList {
private List<Candidate> list = new ArrayList<>();
public void addCandidate(Candidate candidate) {
list.add(candidate);
}
//遍历访问候选人集合中的每一个对象
public void accept(Award handler) {
for (Candidate c : list) {
c.accept(handler);
}
}
}
//客户端
public class Client {
public static void main(String[] args) {
/**
* 案例需求描述:
* 某公司为某高校开发一套奖励审批系统,该系统可以实现教师奖励和学生奖励的审批,
* 如果教师发表论文数操作10篇或者学生论文数超过两篇可以平科研奖;
* 如果教师教学反馈分大于等于90分或者学生平均成绩大于等于90分可以评选成绩优秀奖。
* 请用访问者模式实现该系统,以判断候选人集合中的教师或学生是否符合某种获奖要求。
*
*/
//定义候选人集合
CandidateList candidateList = new CandidateList();
Candidate s1,s2,s3,t1,t2,t3;
s1 = new Student("刘备",70.0,1);
s2 = new Student("张飞",78.0,3);
s3 = new Student("关羽",91.5,0);
t1 = new Teacher("诸葛亮",95.0,0);
t2 = new Teacher("庞统",75.0,11);
t3 = new Teacher("周瑜",88.5,6);
candidateList.addCandidate(s1);
candidateList.addCandidate(s2);
candidateList.addCandidate(s3);
candidateList.addCandidate(t1);
candidateList.addCandidate(t2);
candidateList.addCandidate(t3);
//以科研奖来访问
Award scientificAward = new ScientificAward();
candidateList.accept(scientificAward);
System.out.println("-----------------------");
//以成绩优秀奖来访问
Award achievementAward = new AchievementAward();
candidateList.accept(achievementAward);
}
}
三、访问者模式的优缺点和适用环境
3.1 访问者模式的优点
- 在访问者模式中增加新的访问操作很方便;
- 将有关元素的访问行为集中到一个访问者对象中,而不是分散在一个个的元素类中,类的职责更加清晰;
- 让用户能够在不修改现有元素类层次结构的情况下定义作用于该层次结构的操作。
3.2 访问者模式的缺点
- 在访问者模式中增加新的元素类很难,违背了开闭原则的要求;
- 破坏了对象的封装性。
3.3 访问者模式的适用环境
- 一个对象结构包含多个类型对象,希望对这些对象实施一些依赖其具体类型的操作;
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类;
- 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
【参考文献】:
本文是根据刘伟的《Java设计模式》一书的学习笔记,仅供学习用途,勿做其他用途,请尊重知识产权。
【本文代码仓库】:https://gitee.com/xiongbomy/java-design-pattern.git