学习路径: https://coding.imooc.com/class/270.html
- 前言
“访问者模式一般开发中比较少用,但是如果需要用到了,该模式将是最佳实现” - 应用场景
网课有免费课程、实战课程(付费)。访问课程的角色有学生、老师。存在以下四种行为:
老师访问免费课程、老师访问付费课程、学生访问免费课程、学生访问付费课程。
代码上如果要拓展访问者的行为,可以考虑采用访问者模式
public static void main(String[] args) {
// 两类课程
Course freeCourse = new FreeCourse("MVC的理解");
Course codingCourse = new CodingCourse("JVM内存分析", 14);
// 两个访问者
Visitor studentVisitor = new StudentVisitor();
Visitor teacherVisitor = new TeacherVisitor();
// 课程容器,抽象容器:Course的子类
ArrayList<Course> courseArrayList = new ArrayList<>();
courseArrayList.add(freeCourse);
courseArrayList.add(codingCourse);
// 访问者容器,抽象容器:Visitor的实现类
ArrayList<Visitor> visitorArrayList = new ArrayList<>();
visitorArrayList.add(studentVisitor);
visitorArrayList.add(teacherVisitor);
// 两种容器进行循化嵌套
for (Visitor visitor : visitorArrayList) {
for (Course course: courseArrayList) {
// 抽象对象(抽象类的子类)调用方法,方法参数也为抽象对象(接口的实现类)
course.accept(visitor);
}
}
}
- 实现
/**
* 课程抽象类
*/
public abstract class Course {
// 开放权限让子类重写
protected String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 定义应用层方法,每个子类自己改写,入参是访问者
* @param visitor
*/
public abstract void accept(Visitor visitor);
}
/**
* 抽象课程的子类
*/
public class FreeCourse extends Course{
public FreeCourse(String name) {
this.name = name;
}
/**
* 核心方法:
* 传入Visitor对象,
* 内部调用Visitor对象的visit方法,参数为调用者(自己)
* 该方法联系了访问者和课程的两个抽象对象
* @param visitor 访问者
*/
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
/**
* 访问者接口,访问不同的课程有不同的行为。
* 该模式下,尽量确定被访问者的数量,不然拓展接口实现会造成大量代码修改
*/
public interface Visitor {
void visit(CodingCourse codingCourse);
void visit(FreeCourse freeCourse);
}
/**
* 实现访问者接口,自定义访问者访问不同课程的业务逻辑
* 如果要新增一个访问者,新增一个类即可
*/
public class StudentVisitor implements Visitor {
@Override
public void visit(CodingCourse codingCourse) {
System.out.println("学生角色查看" + "实战课程: "+ codingCourse.getName());
}
@Override
public void visit(FreeCourse freeCourse) {
System.out.println("学生角色查看" + "实战课程: "+ freeCourse.getName());
}
}
- 拓展
应用层的一段代码,可以修改主被动关系
for (Visitor visitor : visitorArrayList) {
for (Course course: courseArrayList) {
// 抽象对象(抽象类的子类)调用方法,方法参数也为抽象对象(接口的实现类)
course.accept(visitor);
}
}
其中course.accept(visitor);
可以改为visitor.visit(course);
,但是需要修改接口实现
/**
* 不同的访问者,有不同的行为
*/
public interface Visitor {
void visit(CodingCourse codingCourse);
void visit(FreeCourse freeCourse);
// jdk1.8 可以在接口写实现
default void visit(Course course){
if(course instanceof CodingCourse){
visit((CodingCourse)course);
} else if(course instanceof FreeCourse){
visit((FreeCourse)course);
} else {
throw new UnsupportedOperationException("访问权限不支持");
}
}
}
- 总结
访问者模式加强了访问者的可拓展性,核心是把访问者作为被访问者一方法的入参,并回调访问者的具体方法,参数为被访问者。缺点是,被访问者如上文提到的课程类别,如果新增一个课程类别就要修改Visitor接口,造成大量的代码修改。所以使用访问者模式的时候应该尽量确定被访者是不需要拓展的。