概述:
访问者模式是一种较为复杂的行为型设计模式,它包含访问者和被访问元素两个主要组成部分,这些被访问的元素通常具有不同的类型,且不同的访问者可以对它们进行不同的访问操作。在使用访问者模式时,被访问元素通常不是单独存在的,它们存储在一个集合中,这个集合被称为“对象结构”,访问者通过遍历对象结构实现对其中存储的元素的逐个操作。
定义:
提供一个作用于某对象结构中的各元素的操作表示,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式是一种对象行为型模式。
结构:
- Vistor(抽象访问者):抽象访问者为对象结构中每一个具体元素类ConcreteElement声明一个访问操作,从这个操作的名称或参数类型可以清楚知道需要访问的具体元素的类型,具体访问者需要实现这些操作方法,定义对这些元素的访问操作。
- ConcreteVisitor(具体访问者):具体访问者实现了每个由抽象访问者声明的操作,每一个操作用于访问对象结构中一种类型的元素。
- Element(抽象元素):抽象元素一般是抽象类或者接口,它定义一个accept()方法,该方法通常以一个抽象访问者作为参数。
- ConcreteElement(具体元素):具体元素实现了accept()方法,在accept()方法中调用访问者的访问方法以便完成对一个元素的操作。
- ObjectStructure(对象结构):对象结构是一个元素的集合,它用于存放元素对象,并且提供了遍历其内部元素的方法。它可以结合组合模式来实现,也可以是一个简单的集合对象,如一个List对象或一个Set对象。
UML图:
场景:
某高校开发一套奖励审批系统,该系统可以实现教师奖励和学生奖励的审批(Award Check),如果教师发表论文数超过10篇或者学生论文超过2篇可以评选科研奖,如果教师教学反馈分大于等于90分或者学生平均成绩大于等于90分可以评选成绩优秀奖。试使用访问者模式设计该系统,以判断候选人集合中的教师或学生是否符合某种获奖要求。
代码分析:
package com.example.vistorpattern;
/**
* Created by **.
* 候选者抽象类
*/
public abstract class Candidate {
private String name;
private String sex;
private String age;
// 身份
private String identity;
// 论文数量
private String numOfPapers;
public Candidate(String name,String sex,String age,String identity,String numOfPapers){
this. name = name;
this. sex = sex;
this. age = age;
this. identity = identity;
this. numOfPapers = numOfPapers;
}
public void setName(String name) {
this. name = name;
}
public String getName() {
return name;
}
public void setSex(String sex) {
this. sex = sex;
}
public String getSex() {
return sex;
}
public void setAge(String age) {
this. age = age;
}
public String getAge() {
return age;
}
public void setIdentity(String identity) {
this. identity = identity;
}
public String getIdentity() {
return identity;
}
public void setNumOfPapers(String numOfPapers) {
this. numOfPapers = numOfPapers;
}
public String getNumOfPapers() {
return numOfPapers;
}
protected abstract void accept(IVistor vistor);
}
/**
* Created by **.
* 候选者抽象类
*/
public abstract class Candidate {
private String name;
private String sex;
private String age;
// 身份
private String identity;
// 论文数量
private String numOfPapers;
public Candidate(String name,String sex,String age,String identity,String numOfPapers){
this. name = name;
this. sex = sex;
this. age = age;
this. identity = identity;
this. numOfPapers = numOfPapers;
}
public void setName(String name) {
this. name = name;
}
public String getName() {
return name;
}
public void setSex(String sex) {
this. sex = sex;
}
public String getSex() {
return sex;
}
public void setAge(String age) {
this. age = age;
}
public String getAge() {
return age;
}
public void setIdentity(String identity) {
this. identity = identity;
}
public String getIdentity() {
return identity;
}
public void setNumOfPapers(String numOfPapers) {
this. numOfPapers = numOfPapers;
}
public String getNumOfPapers() {
return numOfPapers;
}
protected abstract void accept(IVistor vistor);
}
package com.example.vistorpattern;
/**
* Created by **
* 学生候选者
*/
public class StudentCandidate extends Candidate {
// 平均成绩
private int averageScore;
public StudentCandidate(String name, String sex, String age, String identity, String numOfPapers) {
super(name, sex, age, identity, numOfPapers);
}
public void setAverageScore( int averageScore) {
this. averageScore = averageScore;
}
public int getAverageScore() {
return averageScore;
}
@Override
protected void accept(IVistor vistor) {
vistor.vistor( this);
}
}
/**
* Created by **
* 学生候选者
*/
public class StudentCandidate extends Candidate {
// 平均成绩
private int averageScore;
public StudentCandidate(String name, String sex, String age, String identity, String numOfPapers) {
super(name, sex, age, identity, numOfPapers);
}
public void setAverageScore( int averageScore) {
this. averageScore = averageScore;
}
public int getAverageScore() {
return averageScore;
}
@Override
protected void accept(IVistor vistor) {
vistor.vistor( this);
}
}
package com.example.vistorpattern;
/**
* Created by **
* 教师候选者
*/
public class TeacherCandidate extends Candidate {
// 教学反馈分
private int teachingFeedbackScore;
public TeacherCandidate(String name, String sex, String age, String identity, String numOfPapers) {
super(name, sex, age, identity, numOfPapers);
}
public void setTeachingFeedbackScore( int teachingFeedbackScore) {
this. teachingFeedbackScore = teachingFeedbackScore;
}
public int getTeachingFeedbackScore() {
return teachingFeedbackScore;
}
@Override
protected void accept(IVistor vistor) {
vistor.vistor( this);
}
}
/**
* Created by **
* 教师候选者
*/
public class TeacherCandidate extends Candidate {
// 教学反馈分
private int teachingFeedbackScore;
public TeacherCandidate(String name, String sex, String age, String identity, String numOfPapers) {
super(name, sex, age, identity, numOfPapers);
}
public void setTeachingFeedbackScore( int teachingFeedbackScore) {
this. teachingFeedbackScore = teachingFeedbackScore;
}
public int getTeachingFeedbackScore() {
return teachingFeedbackScore;
}
@Override
protected void accept(IVistor vistor) {
vistor.vistor( this);
}
}
package com.example.vistorpattern;
/**
* Created by **
* 访问者抽象类
*/
public interface IVistor {
public void vistor(TeacherCandidate teacherCandidate);
public void vistor(StudentCandidate studentCandidate);
}
/**
* Created by **
* 访问者抽象类
*/
public interface IVistor {
public void vistor(TeacherCandidate teacherCandidate);
public void vistor(StudentCandidate studentCandidate);
}
package com.example.vistorpattern;
import com.example.logutils.LogFactory;
/**
* Created by **.
* 具体访问者
*/
public class Vistor implements IVistor {
@Override
public void vistor(TeacherCandidate teacherCandidate) {
LogFactory. log( " 教师信息: "+getTeacherInfo(teacherCandidate));
}
@Override
public void vistor(StudentCandidate studentCandidate) {
LogFactory. log( " 学生信息: "+getStudentInfo(studentCandidate));
}
/**
* 获取基本信息
* @param candidate
* @return
*/
private String getBasicInfo(Candidate candidate){
String info = "";
String name = " 姓名: "+candidate.getName();
String sex = " 性别: "+candidate.getSex();
String age = " 年龄: "+candidate.getAge();
String identity = " 身份: "+candidate.getIdentity();
String numOfPapers = " 发表论文数量: "+candidate.getNumOfPapers();
info = name + " \t " + sex + " \t " + age + " \t " + identity + " \t " + numOfPapers + " \t ";
return info;
}
/**
* 获取教师信息
* @param teacherCandidate
* @return
*/
private String getTeacherInfo(TeacherCandidate teacherCandidate){
int teachingFeedbackScore = teacherCandidate.getTeachingFeedbackScore();
String teachingFBScor = " 教学反馈分数: "+teachingFeedbackScore;
String teacherInfo = this.getBasicInfo(teacherCandidate)+teachingFBScor;
return teacherInfo;
}
/**
* 获取学生信息
* @param studentCandidate
* @return
*/
private String getStudentInfo(StudentCandidate studentCandidate){
int averageScore = studentCandidate.getAverageScore();
String studentScore = " 平均成绩: "+averageScore;
String studentInfo = this.getBasicInfo(studentCandidate) + studentScore;
return studentInfo;
}
}
import com.example.logutils.LogFactory;
/**
* Created by **.
* 具体访问者
*/
public class Vistor implements IVistor {
@Override
public void vistor(TeacherCandidate teacherCandidate) {
LogFactory. log( " 教师信息: "+getTeacherInfo(teacherCandidate));
}
@Override
public void vistor(StudentCandidate studentCandidate) {
LogFactory. log( " 学生信息: "+getStudentInfo(studentCandidate));
}
/**
* 获取基本信息
* @param candidate
* @return
*/
private String getBasicInfo(Candidate candidate){
String info = "";
String name = " 姓名: "+candidate.getName();
String sex = " 性别: "+candidate.getSex();
String age = " 年龄: "+candidate.getAge();
String identity = " 身份: "+candidate.getIdentity();
String numOfPapers = " 发表论文数量: "+candidate.getNumOfPapers();
info = name + " \t " + sex + " \t " + age + " \t " + identity + " \t " + numOfPapers + " \t ";
return info;
}
/**
* 获取教师信息
* @param teacherCandidate
* @return
*/
private String getTeacherInfo(TeacherCandidate teacherCandidate){
int teachingFeedbackScore = teacherCandidate.getTeachingFeedbackScore();
String teachingFBScor = " 教学反馈分数: "+teachingFeedbackScore;
String teacherInfo = this.getBasicInfo(teacherCandidate)+teachingFBScor;
return teacherInfo;
}
/**
* 获取学生信息
* @param studentCandidate
* @return
*/
private String getStudentInfo(StudentCandidate studentCandidate){
int averageScore = studentCandidate.getAverageScore();
String studentScore = " 平均成绩: "+averageScore;
String studentInfo = this.getBasicInfo(studentCandidate) + studentScore;
return studentInfo;
}
}
package com.example.vistorpattern;
import java.util.ArrayList;
import java.util.List;
/**
* Created by **.
* 相当于 objectStructure
*/
public class CandidateList {
private List<Candidate> list = new ArrayList<Candidate>();
public void addCandidate(Candidate candidate){
list.add(candidate);
}
public void accept(Vistor vistor){
for (Candidate candidate : list){
candidate.accept(vistor);
}
}
}
import java.util.ArrayList;
import java.util.List;
/**
* Created by **.
* 相当于 objectStructure
*/
public class CandidateList {
private List<Candidate> list = new ArrayList<Candidate>();
public void addCandidate(Candidate candidate){
list.add(candidate);
}
public void accept(Vistor vistor){
for (Candidate candidate : list){
candidate.accept(vistor);
}
}
}
客户端调用:
TeacherCandidate teacher_zhang =
new TeacherCandidate(
"
张大帅
",
"
男
",
"48",
"
教师
",
"20");
TeacherCandidate teacher_li = new TeacherCandidate( " 李娟 ", " 女 ", "52", " 教师 ", "8");
teacher_zhang.setTeachingFeedbackScore( 88);
teacher_li.setTeachingFeedbackScore( 95);
StudentCandidate studen_x = new StudentCandidate( " 小明 ", " 男 ", "22", " 学生 ", "1");
StudentCandidate studen_l = new StudentCandidate( " 小丽 ", " 女 ", "21", " 学生 ", "3");
studen_x.setAverageScore( 70);
studen_l.setAverageScore( 97);
CandidateList candidateList = new CandidateList();
candidateList.addCandidate(teacher_zhang);
candidateList.addCandidate(teacher_li);
candidateList.addCandidate(studen_l);
candidateList.addCandidate(studen_x);
candidateList.accept( new Vistor());
TeacherCandidate teacher_li = new TeacherCandidate( " 李娟 ", " 女 ", "52", " 教师 ", "8");
teacher_zhang.setTeachingFeedbackScore( 88);
teacher_li.setTeachingFeedbackScore( 95);
StudentCandidate studen_x = new StudentCandidate( " 小明 ", " 男 ", "22", " 学生 ", "1");
StudentCandidate studen_l = new StudentCandidate( " 小丽 ", " 女 ", "21", " 学生 ", "3");
studen_x.setAverageScore( 70);
studen_l.setAverageScore( 97);
CandidateList candidateList = new CandidateList();
candidateList.addCandidate(teacher_zhang);
candidateList.addCandidate(teacher_li);
candidateList.addCandidate(studen_l);
candidateList.addCandidate(studen_x);
candidateList.accept( new Vistor());
log输出:
11-08 15:01:54.142 31655-31655/? D/test: 教师信息:姓名:张大帅 性别:男 年龄:48 身份:教师 发表论文数量:20 教学反馈分数:88
11-08 15:01:54.142 31655-31655/? D/test: 教师信息:姓名:李娟 性别:女 年龄:52 身份:教师 发表论文数量:8 教学反馈分数:95
11-08 15:01:54.142 31655-31655/? D/test: 学生信息:姓名:小丽 性别:女 年龄:21 身份:学生 发表论文数量:3 平均成绩:97
11-08 15:01:54.142 31655-31655/? D/test: 学生信息:姓名:小明 性别:男 年龄:22 身份:学生 发表论文数量:1 平均成绩:70
优点:
- 增加新的访问操作很方便。使用访问者模式,增加新的访问操作就意味着增加一个新的具体访问者类,实现简单,无须修改源代码,符合“开闭原则”。
- 将有关元素对象的访问行为集中到一个访问者对象中,而不是分散在一个个的元素类中。类的职责更加清晰,有利于对象结构中元素对象的复用,相同的对象结构可以供多个不同的访问者访问。
- 让用户能够在不修改现有元素类层次结构的情况下,定义作用于该层次结构的操作。
缺点:
- 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”的要求。
- 破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法供访问者访问。
适用场景:
- 一个对象结构包含多个类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。访问者模式使得我们可以将相关的访问操作集中起来定义在访问者类中,对象结构可以被多个不同的访问者类所使用,将对象本身与对象的访问操作分离。
- 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
扩展:
在访问者模式中,包含一个用于存储元素对象集合的对象结构,我们通常可以使用迭代器来遍历对象结构,同时具体元素之间可以存在整体与部分关系,有些元素作为容器对象,有些元素作为成员对象,可以使用组合模式来组织元素。引入组合模式后的访问者模式结构图如图所示: