设计模式系列篇(十八)——访问者模式
2020-09-06 本文已影响0人
复旦猿
What
访问者模式(Visitor Pattern),允许一个或者多个操作应用到一组对象上,解耦操作和对象本身。我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
Why
- 符合单一职责原则。 访问者模式将操作和对象解耦,每个类的职责非常单一。
- 优秀的扩展性。 如果想增加操作类型,无须对已有的稳定的对象类进行更改,具有很好的扩展性。
- 灵活性。优秀扩展性的必然结果,使得增加或删除操作类型都很方便。
When
- 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。
How
访问者模式的实现是比较难理解的,但访问者的实现大家不必掌握,只需要见到能认出是访问者模式就好了。
下面,我们以实验室年底考核产生报表为例介绍访问者模式的实现。年底了,你的实验室迎来了一年一度的考核,实验室参与年终考核的有学硕和专硕,考核官包括导师和辅导员。对于导师来说,他关注的是学生的科研或者项目情况,而辅导员则更关注学生的课程成绩和社会实践。在这个例子中,学硕和专硕就是对象;而学生的科研、项目情况就是操作,而访问者的类型包括导师和辅导员。
UML图如下所示:
访问者模式
包含以下几部分:
- Visitor:接口或者抽象类,定义了对每个 Master子类 访问的行为,它的参数就是被访问的元素,它的方法个数理论上与元素的个数是一样的,因此,访问者模式要求元素的类型要稳定,如果经常添加、移除元素类,必然会导致频繁地修改 Visitor 接口,如果出现这种情况,则说明不适合使用访问者模式。
- ConcreteVisitor:具体的访问者,如本例中的MentorVisitor,它需要给出对每一个元素类访问时所产生的具体行为。
- Master:元素接口或者抽象类,它定义了一个接受访问者(accept)的方法,其意义是指每一个元素都要可以被访问者访问。
- AcademicMaster、EngineerMaster:具体的元素类,它提供接受访问的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
具体代码如下:
首先是Master及其子类。
// 硕士抽象类,定义基本属性,并定义accept方法,参数为 Vistor 对象。
public abstract class Master {
private String name;
private Double gpa;
private Double socialPractice;
private Integer paperCount;
private Integer projectCount;
public Master(String name, Double gpa, Double socialPractice, Integer paperCount, Integer projectCount) {
this.name = name;
this.gpa = gpa;
this.socialPractice = socialPractice;
this.paperCount = paperCount;
this.projectCount = projectCount;
}
// 省略getter方法
public abstract void accept(Visitor visitor);
}
// 学硕类
public class AcademicMaster extends Master {
public AcademicMaster(String name, Double gpa, Double socialPractice, Integer paperCount, Integer projectCount) {
super(name, gpa, socialPractice, paperCount, projectCount);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 专硕类
public class EngineerMaster extends Master {
public EngineerMaster(String name, Double gpa, Double socialPractice, Integer paperCount, Integer projectCount) {
super(name, gpa, socialPractice, paperCount, projectCount);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
接下来是Visitor接口及其实现类。
public interface Visitor {
void visit(AcademicMaster academicMaster);
void visit(EngineerMaster engineerMaster);
}
// 导师报表
public class MentorVisitor implements Visitor {
@Override
public void visit(AcademicMaster academicMaster) {
System.out.println(String.format("name: %s, paper count: %d",
academicMaster.getName(), academicMaster.getPaperCount()));
}
@Override
public void visit(EngineerMaster engineerMaster) {
System.out.println(String.format("name: %s, project count: %d",
engineerMaster.getName(), engineerMaster.getProjectCount()));
}
}
// 辅导员报表
public class CounselorVisitor implements Visitor {
@Override
public void visit(AcademicMaster academicMaster) {
System.out.println(String.format("name: %s, gpa: %f",
academicMaster.getName(), academicMaster.getGpa()));
}
@Override
public void visit(EngineerMaster engineerMaster) {
System.out.println(String.format("name: %s, social practice: %f",
engineerMaster.getName(), engineerMaster.getSocialPractice()));
}
}
通过上面两部分代码,大家可以看出,对Master类对象的操作都集中在Visitor实现中,这就实现了对象元素和操作解耦。如果增加访问者,如家长或者院领导都无须修改Master类,只需要增加相应的访问者实现就可以了。
最后,给个测试类。
public class TestMain {
public static void main(String[] args) {
Master academicMaster1 = new AcademicMaster("Jeremy", 3.7, 3.0, 1, 3);
Master academicMaster2 = new AcademicMaster("Bob", 3.2, 2.0, 2, 1);
Master engineerMaster1 = new EngineerMaster("Tom", 3.3, 4.0, 0, 1);
Master engineerMaster2 = new EngineerMaster("Amy", 3.0, 3.0, 1, 2);
List<Master> masters = new ArrayList<>();
masters.add(academicMaster1);
masters.add(academicMaster2);
masters.add(engineerMaster1);
masters.add(engineerMaster2);
System.out.println("-----mentor's report-----");
Visitor mentorVisitor = new MentorVisitor();
for (Master master : masters) {
master.accept(mentorVisitor);
}
System.out.println("-----counselor's report-----");
Visitor counselorVisitor = new CounselorVisitor();
for (Master master : masters) {
master.accept(counselorVisitor);
}
}
}
输出如下:
-----mentor's report-----
name: Jeremy, paper count: 1
name: Bob, paper count: 2
name: Tom, project count: 1
name: Amy, project count: 2
-----counselor's report-----
name: Jeremy, gpa: 3.700000
name: Bob, gpa: 3.200000
name: Tom, social practice: 4.000000
name: Amy, social practice: 3.000000
搞定。
代码地址
写在最后
如果你觉得我写的文章帮到了你,欢迎点赞、评论、分享、赞赏哦,你们的鼓励是我不断创作的动力~