Java设计模式大作业——访问者模式
系统以web网页设计为主,采用的技术架构为服务端SpringBoot+Mybatis-plus+Satoken
,客户端(Web)Vue3+Element-plus+Echarts
.
系统对于不同不同访问者身份存于mysql
数据库,登陆后系统自动识别访问者身份并显示对应内容
一.应用场景与案例描述
访问者模式是一种行为设计模式,其主要目的是封装一组可能会经常变化的操作,使这些操作可以在不改变被操作对象的类的前提下应用于这些对象。它通过在被访问的元素类中添加一个接受访问者的方法,使得访问者可以访问被访问者的元素。
在校园中,对于一个学院的师生,辅导员更为关心同学们的成绩以及基本个人信息,学业主观努力度、系主任则关注教师的满意度,学生的学分修读情况,学校则关注该学院的师资力量与学生整体学习情况,企业关注学生的修读课程水平以及教资力量。这些整体都是对于一个学院的师生进行不同角度查阅信息,因此在不同的场景下可以构造不同访问者。
二.案例分析与解决问题
案例分析
角色定义:
学生:包含成绩、课程修读情况、基本个人信息等。
教师:包含个人信息、职称学生满意度等。
学院:包含师资力量、学生整体学习情况等。
企业:关注学生的修读课程水平和教资力量。
辅导员:关注学生的基本个人信息,以及任课教师基本信息
系主任:关注学生整体课程要求完成的、教师教学满意度
问题描述:
不同角色对相同的学院信息有不同的关注点。
不同角色需要执行不同的操作,比如计算学生成绩、查看教师满意度等。
为什么使用访问者模式:
解耦合适: 访问者模式将数据结构和操作分离,每个角色都可以定义自己的访问者。这样,当需要新增一种角色或者新增一种操作时,不需要修改已有的代码。
易扩展性: 可以方便地新增访问者,而不影响已有的数据结构和其他访问者。
单一职责原则: 每个访问者负责执行一种操作,保证了每个类的职责单一。
使用访问者模式的优势:
灵活性: 不同的访问者可以根据自己的需要定义自己的操作,而无需修改被访问的对象(学院、学生、教师等)。
可扩展性: 当需要新增一种角色或者新增一种操作时,只需要添加对应的访问者,而不需要修改已有代码。
维护性: 操作和数据结构分离,使得系统更易维护和理解。
使用访问者模式可以有效地解决不同角色对于相同对象的不同关注点,提高了系统的灵活性和可维护性。
三.各个角色描述与UML图示
基本类Person
包括每个人的基本信息,编号、姓名、性别、年龄、所属部门、联系方式。
对于学生类Student
还包括个人平均成绩、所有修读课程成绩。
对于教师类Teacher
还包括教学满意度、职称。
元素类在基本方法外需要添加一个accept()
方法
对于访问者Vistor而言,辅导员Counsellor
关注学生基本个人信息,还有平均成绩以及排名、任课教师的基本个人信息包括联系方式、姓名、所属部门;系主任Principal
关注整个学院的教资力量以及学生课程整体修读情况,包括不同职称教师人数、教评满意度、学生专业修读学分完成度。访问者分别实现对不同角色的访问方法visit()
,将整个学院的师生用Department
中的ArrayList<Person>
装载,接受不同访问者实现不同操作后数据返回给客户端。
整体UML
图如下
四.程序完整源代码
源代码较多,仅给出关键访问者模式设计代码
抽象元素类Person,记录个人基本信息,编号、姓名、性别、年龄、所属部门、联系方式。
Person.java
/**
* @author zouran
* createDate:2023/12/23 17:27
*/
@Component
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "person", discriminatorType = DiscriminatorType.STRING)
@Data
@Accessors(chain = true)
public abstract class Person implements Serializable {
@TableId(type = IdType.AUTO)
private Integer id;
private String name;
private String sex;
private Integer age;
private String department;
private String phone;
/**
* @return 人物身份
*/
public abstract String getIdentify();
public abstract Map<String, Object> accept(Visitor visitor);
}
学生类,继承Person
类,实现accept
方法,新增成员List<Grade> gradeList
记录个人所有的修读课程成绩。
Student.java
/**
* @author zouran
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
@Component
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@DiscriminatorValue("student")
@ToString(callSuper = true)
public class Student extends Person {
private Double gradeAverage;
@TableField(exist = false)
private List<Grade> gradeList = new ArrayList<>();
/**
* @return 人物身份
*/
@Override
public String getIdentify() {
return "student";
}
public List<Map<String, Object>> getAllGrades() {
List<Map<String, Object>> maps = new ArrayList<>();
for (Grade grade : this.gradeList) {
Map<String, Object> map = new HashMap<>();
map.put("name", this.getName());
map.put("courseId", grade.getCourseId());
map.put("term", grade.getSchoolYear() + "-" + (grade.getSchoolYear() + 1) + (grade.getTerm() == 1 ? "-秋" : "-春"));
map.put("courseName", grade.getCourse().getCourseName());
map.put("teacherName", grade.getCourse().getTeacher().getName());
map.put("score", grade.getScore());
map.put("gradePoint", grade.getGradePoint());
map.put("credit", grade.getCourse().getCredit());
maps.add(map);
}
return maps;
}
@Override
public Map<String, Object> accept(Visitor visitor) {
return visitor.visit(this);
}
}
教师类,继承Person
类,实现accept
方法,新增成员记录职称、学生满意度。
Teacher.java
/**
* @author zouran
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
@Component
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@DiscriminatorValue("teacher")
@ToString(callSuper = true)
public class Teacher extends Person {
private Double satisfaction;
private String title;
/**
* @return 人物身份
*/
@Override
public String getIdentify() {
return "teacher";
}
@Override
public Map<String, Object> accept(Visitor visitor) {
return visitor.visit(this);
}
}
访问者接口,针对不同元素类定义多个visit
方法,接受参数不同。
Visitor.java
/**
* @author zouran
* createDate:2023/12/26 12:30
*/
public interface Visitor {
Map<String, Object> visit(Student student);
Map<String, Object> visit(Teacher teacher);
}
辅导员类,实现父类的visit
方法,对于访问学生,返回学生学号、姓名、性别、年龄、部门、所有课程成绩,还有平均成绩、任课教师的基本个人信息包括联系方式、姓名、所属部门。
Counsellor.java
/**
* @author zouran
* createDate:2023/12/26 12:29
*/
public class Counsellor implements Visitor {
@Override
public Map<String, Object> visit(Student student) {
List<Map<String, Object>> maps = student.getAllGrades();
double sumCredit = 0.0;
double sumGradePoint = 0.0;
for (Map<String, Object> map : maps) {
sumGradePoint += (double) map.get("credit") * (double) map.get("gradePoint");
sumCredit += (double) map.get("credit");
}
Map<String, Object> map = new HashMap<>();
map.put("identify", student.getIdentify());
map.put("id", student.getId());
map.put("name", student.getName());
map.put("sex", student.getSex());
map.put("age", student.getAge());
map.put("department", student.getDepartment());
// map.put("gradeAverage",student.getGradeAverage());
map.put("allGrades", student.getAllGrades());
map.put("phone", student.getPhone());
map.put("gradeAverage", String.format("%.2f", sumGradePoint / sumCredit));
return map;
}
@Override
public Map<String, Object> visit(Teacher teacher) {
Map<String, Object> map = new HashMap<>();
map.put("identify", teacher.getIdentify());
map.put("name", teacher.getName());
map.put("department", teacher.getDepartment());
map.put("phone", teacher.getPhone());
return map;
}
}
系主任类,实现父类的visit
方法,对于访问学生,返回学生学号、姓名、部门、总的课程修读学分和。教师编号、姓名、部门、教评满意度、职称、联系方式
Principal.java
/**
* @author zouran
* createDate:2023/12/26 12:30
*/
public class Principal implements Visitor {
@Override
public Map<String, Object> visit(Student student) {
List<Map<String, Object>> maps = student.getAllGrades();
double sumCredit = 0.0;
for (Map<String, Object> map : maps) {
if ((double) map.get("score") >= 60)
sumCredit += (double) map.get("credit");
}
Map<String, Object> map = new HashMap<>();
map.put("identify", student.getIdentify());
map.put("id", student.getId());
map.put("name", student.getName());
map.put("department", student.getDepartment());
map.put("sumCredit", sumCredit);
return map;
}
@Override
public Map<String, Object> visit(Teacher teacher) {
Map<String, Object> map = new HashMap<>();
map.put("identify", teacher.getIdentify());
map.put("id", teacher.getId());
map.put("name", teacher.getName());
map.put("department", teacher.getDepartment());
map.put("satisfaction", teacher.getSatisfaction());
map.put("title", teacher.getTitle());
map.put("phone", teacher.getPhone());
return map;
}
}
对象结构类,装载所有Person
实例,相当于容纳了一个学院所有师生信息,information()
返回师生信息
Department.java
/**
* @author zouran
* createDate:2023/12/26 0:34
*/
@Data
public class Department implements Serializable {
private final List<Person> personList = new ArrayList<>();
public Department() {
}
public Department(List<Teacher> teachers, List<Student> students) {
personList.addAll(teachers);
personList.addAll(students);
}
public List<Map<String, Object>> information(Visitor visitor) {
List<Map<String, Object>> maps = new ArrayList<>();
for (Person person : personList) {
maps.add(person.accept(visitor));
}
return maps;
}
}
客户端类,调用数据
VisitorController.java
/**
* 访问者接口
*
* @author zouran
* createDate:2023/12/27 23:49
*/
@RestController
@RequestMapping("/visitor")
public class VisitorController {
private final IStudentService iStudentService;
private final ITeacherService iTeacherService;
private final ResultMap<Object> resultMap;
public VisitorController(IStudentService iStudentService, ITeacherService iTeacherService, ResultMap<Object> resultMap) {
this.iStudentService = iStudentService;
this.iTeacherService = iTeacherService;
this.resultMap = resultMap;
}
private Map<String, Object> classify(List<Map<String, Object>> list) {
Map<String, Object> maps = new HashMap<>();
List<Map<String, Object>> students = new ArrayList<>();
List<Map<String, Object>> teachers = new ArrayList<>();
for (Map<String, Object> stringObjectMap : list) {
if (stringObjectMap.get("identify") == "student") {
stringObjectMap.remove("identify");
students.add(stringObjectMap);
}
if (stringObjectMap.get("identify") == "teacher") {
stringObjectMap.remove("identify");
teachers.add(stringObjectMap);
}
}
maps.put("student", students);
maps.put("teacher", teachers);
return maps;
}
/**
* 辅导员视角
*
* @return 辅导员视角
*/
@RequestMapping("/counsellor")
public ResultMap<Object> counsellorQueryAll() {
System.out.println(StpUtil.getPermissionList());
return resultMap.success(classify(
new Department(iTeacherService.list(null), iStudentService.queryAll()).information(new Counsellor())));
}
/**
* 校长视角
*
* @return 校长视角
*/
@RequestMapping("/principal")
public ResultMap<Object> principalQueryAll() {
return resultMap.success(classify(
new Department(iTeacherService.list(null), iStudentService.queryAll()).information(new Principal())));
}
}
五.程序运行结果
1.登录页面
2.辅导员登录后页面显示
2.1查看学生基本信息,学生学号、姓名、性别、年龄、部门、平均成绩。系统会可视化个人信息情况,如各专业人数以及占比,男女比例
2.2对表格数据筛选,仅查看软件工程专业学生信息
2.3查看同学课程成绩,系统利用echarts
可视化各科目挂科人数以及优秀人数
2.4仅查看挂科课程成绩情况,包括学生名字、课程名、教师名等信息
2.5查看教师姓名、联系方式、所属部门
2.6除此之外,表格数据可以进行排序,如将软件工程学生个人平均成绩从高到低
3.系主任视角
3.1查看学生信息,可以看到学号、姓名、总的已修读学分、所属专业。同时可视化了各专业未达到毕业要求学分人数和占比分布
3.2筛选修读学分不达标同学信息
3.3筛选软件工程修读学分不达标同学信息
3.4查看教资力量统计,会统计不同职称的教师人数,用直方图和饼状图可视化加载,同时可以看到教师的满意度情况
3.5筛选职称为讲师且满意度为非常满意的教师
3.6筛选软件教师信息