行为型模式-访问者模式
1.概述
1.1 引入
对于相同的对象,不同的角色可能会有不同的操作。比如在一个网站论坛中的留言帖,如果是普通游客,那么可能就只会基本的浏览查看功能;如果是注册会员,那么除了浏览功能还能够进行留言和回复;如果是论坛管理员,那么还具备删除帖子的权限。
在这里,各种角色就是访问者,留言帖就是被访问的对象,不同的访问者对同样的对象会有不同的操作,而且还可能增加新的访问者。
1.2 定义
访问者模式(Visitor Pattern)用于表示一个作用于某对象结构中各元素的操作,它使得用户可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
1.2 原理
2.类图
3.角色
Visitor 表示抽象访问者,为对象结构类中每一个ConcreteElement的类声明一个Visit操作 ConcreteVisitor 表示具体访问者 Element 表示抽象元素 ConcreteElement 表示具体元素 ObjectStructure 表示对象结构,提供了一个高层接口以允许访问者访问它的元素
4.优缺点
4.1 优点
1.符合单一职责原则 2.优秀的扩展性,增加新的操作很容易(意味着增加一个新的访问者类) 3.灵活性 4.2 缺点
1.具体元素对访问者公布细节,违反了迪米特原则,在一定程度上破坏了封装性 2.增加具体元素对象很困难,每增加一个新的具体元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体
访问者类中增加新的具体操作,违反了开闭原则
3.违反了依赖倒置原则,依赖了具体类,没有依赖抽象
5.案例
5.1 说明
实现一个审批系统,如果教师的论文发表数量大于等于10篇或学生的论文发表数量大于等于2篇,则可以评选科研奖;如果教师的教学反馈分大于等于90分或者学生的平均成绩大于等于90分,则可以评选成绩优秀奖。
5.2 代码
1)抽象访问者类:
/** * 抽象访问者类:抽象奖励审批类 * @author luckyliuqs */ public abstract class AwardCheck { public abstract void visit(Teacher teacher); public abstract void visit(Student student); }
2)具体访问者类:
/** * 具体访问者类:科研奖审批类 * @author luckyliuqs */ public class ScientificAwardCheck extends AwardCheck{ @Override public void visit(Teacher teacher) { if(teacher.getPaperAmount() >= 10) { //教师论文发表数量大于等于10篇 System.out.println(teacher.getName() + "可评选教师科研奖"); } } @Override public void visit(Student student) { if(student.getPaperAcount() >= 2) { //学生论文发表数量大于等于2篇 System.out.println(student.getName() + "可评选学生科研奖"); } } }
/** * 具体访问者:成绩优秀奖审批类 * @author luckyliuqs */ public class ExcellentAwardCheck extends AwardCheck{ @Override public void visit(Teacher teacher) { if(teacher.getFeedbackScore() >= 90) { //教师教学反馈分大于等于90分 System.out.println(teacher.getName() + "可评选教师成绩优秀奖"); } } @Override public void visit(Student student) { if(student.getScore() >= 90) { //学生平均成绩大于等于90 System.out.println(student.getName() + "可评选学生成绩优秀奖"); } } }
3)抽象元素类:
/** * 抽象元素类:申请人类 * @author luckyliuqs */ public interface Person { //调用访问者(奖项审批类)的访问方法以便完成对一个元素(申请人)的操作 public void accept(AwardCheck check); }
4)具体元素类:
/** * 具体元素类:教师类 * @author luckyliuqs */ public class Teacher implements Person { private String name; //名称 private int paperAmount; //论文发表数量 private double feedbackScore; //教学反馈分 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPaperAmount() { return paperAmount; } public void setPaperAmount(int paperAmount) { this.paperAmount = paperAmount; } public double getFeedbackScore() { return feedbackScore; } public void setFeedbackScore(double feedbackScore) { this.feedbackScore = feedbackScore; } @Override public void accept(AwardCheck check) { check.visit(this); } }
/** * 具体元素类:学生类 * @author luckyliuqs */ public class Student implements Person{ private String name; //名称 private int paperAcount; //论文发表数量 private double score; //平均成绩 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPaperAcount() { return paperAcount; } public void setPaperAcount(int paperAcount) { this.paperAcount = paperAcount; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } @Override public void accept(AwardCheck check) { check.visit(this); } }
5)对象结构类
/** * 对象结构:候选人集合类 * @author luckyliuqs */ public 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<Person> iterator = list.iterator(); while (iterator.hasNext()) { iterator.next().accept(check); } } }
6)客户端类:
/** * 客户端测试类 * @author luckyliuqs */ public class Client { public static void main(String[] args) { CandidateList list = new CandidateList(); AwardCheck scientficCheck = new ScientificAwardCheck(); AwardCheck excellentCheck = new ExcellentAwardCheck(); Teacher teacher = new Teacher(); teacher.setName("教师甲"); teacher.setPaperAmount(16); teacher.setFeedbackScore(98); Student student = new Student(); student.setName("学生乙"); student.setPaperAcount(1); student.setScore(95); list.addPerson(teacher); list.addPerson(student); list.accept(scientficCheck); list.accept(excellentCheck); } }
5.3 效果
教师甲可评选教师科研奖 教师甲可评选教师成绩优秀奖 学生乙可评选学生成绩优秀奖
5.4 分析
可以看出,访问者调用了具体元素的方法,具体元素必须暴露一些内部操作状态,否则访问者的访问就没有了意义。