目录
(1)前言
(2)设计与分析
(3)踩坑心得
(4)改进建议
(5)总结
正文
(1)前言
题目 | 知识点 | 题量(5分为满) | 难度(5分为满) |
选课1 |
1.正则表达式的基本使用(选课1要求会读懂)。 2.多次if-else完成业务判断。 3.解析字符串处理一般业务(之后我会总结到正文第五点总结部分)。 4.简单的继承多态,以及基本的组合。 5.自己要会写正则表达式来匹配用户的命令(选课2要求会写正则表达式)(选课3增加了新业务,也需要新的正则表达式)。 6.排序。尤其是按照中文拼音排序(这里只要求简单的中文排序,不包括多音字) |
3分 | 2分 |
选课2 | 3.5分 | 3分 | |
选课3 | 3分 | 4分(测试点不好过) |
(2)设计与分析
SourceMonitor分析
选课1 |
整个工程式是极度复杂的,所以复杂度高(Max Complexity),是正常的。即便超过SourceMonitor给的区间范围,但是这是程序的需要。比如操作系统,一些软件,我相信放到代码质量检测工具,也会显示代码最大的复杂度超标。
方法量适中,类的数量也是适中的。不会出现什么类爆炸,这种糟糕的现象。
有些方法也没有写的冗长冗长的,一条龙解决非常难的问题。而是独立了很多个方法,封装好。调用多种不同的方法来配合地完成一件业务。平均的stmts(AVG statements)或者说深度(AVG Depth)或者说复杂度(AVG complexity)就是最好的体现。
SourceMonitor平均代码行数的统计说超过200行以上的只有一个类(字符串解析类ParseInput)。除此之外所有类都在100行一下,其中的一半类都甚至小于50行。 是我的类设计的太简单了吗?我后来想了想,这个类就是这么简单,这个类的最大体现,就差不多100行以下的代码就是最佳的体现了,就存一下信息(data)就行了。
这就是我上述对于SM(SourceMonitor)图表的大概解释。
|
|
选课2 | ||
选课3 |
类图分析
选课1 |
由题目可知,最基本最主要的类是选课类(SelectCourse),而一门选课要课程(Course)的信息、学生(Student)的信息以及成绩(Score)的信息。由于成绩要分为考察成绩(InspectionScore),和考试成绩(ExaminationScore),就让这个两个成绩继承Score(成绩类)。最后要按照每个班级输出信息,所以还需要一个StudentClass(学生班级类)。
其中类的成员就不具体叙述,如图所述。
这里又一大亮点就是成绩类,有两个类继承,这就能在选课类中体现多态(SeclectCourse中),以后万一多增加了成绩的类别,就可以把增加的成绩类别继承Score这个类。
三次作业类的基本关系没变。就是方法有变化,我将在下面详细叙述。 |
|
选课2 | ||
选课3 |
解读代码一(Main类)
查看代码
public class Main {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
String s_record = s.nextLine();
ParseInput handle=new ParseInput();
while (!s_record.equals("end")) {
handle.parseInput(s_record);//解析用户输入的每一行数据
s_record = s.nextLine();
}
handle.showStudents();
handle.showCourses();
handle.showClasses();
}
}
用户输入对应的一行字符串,就需要解析用户输入的每一行数据,当用户输入end为止就结束。最后按照用户输入输出所有学生对应信息,所有课程对应信息,所有班级对应信息。
解读代码二(ParseInput类的parseInput方法)
查看代码
public void parseInput(String line) {
String[] sections = line.split(" ");
int ln = sections.length;
if (line.matches(courseInput)) {
courseManipulating(sections, ln);
} else if (line.matches(courseInput2)) {
courseManipulating2(sections, ln);
} else if (line.matches(scoreInput)) {
scoreManipulating(sections, ln);
} else if (line.matches(experimentInput)) {
experimentManipulating(sections, ln);
} else {
System.out.println("wrong format");
}
}
其中用到的的正则表达式
查看代码
static String stuNumMatching = "[0-9]{8}";
static String stuNameMatching = "\\S{1,10}";
static String scoreMatching = "(100|[1-9]?[0-9])";
static String experimentMatching = "[4-9]";
static String courseNameMatching = "\\S{1,10}";
static String courseTypeMatching = "(选修|必修|实验)";
static String checkcourseTypeMatching = "(考试|考察|实验)";
static String courseInput = courseNameMatching + " " + courseTypeMatching + " " + checkcourseTypeMatching;
static String courseInput2 = "\\S{1,10} 实验 实验 [4-9][\\S|\\s]*";
static String scoreInput = stuNumMatching + " " + stuNameMatching + " " + courseNameMatching + " " + scoreMatching
+ " ?" + scoreMatching + "?";
static String experimentInput = "[0-9]{8} \\S{1,10} \\S{1,10}( (100|[1-9]?[0-9]))*";
将用户输入的字符串,以空格分隔成String数组,以后要用。
如果用户输入的字符串是有关添加课程的,我将这行字符串转换成对应的数据,进行课程添加。(对应courseManipulating(),courseManipulating2()这两个方法)。
如果用户输入的字符串树有关学生选课的,我将这行字符串转换成对应的数据,进行学生选课。(对应scoreManipulating(),experimentManipulating()这两个方法)
因为一般的课程添加用courseManipulating(),而像实验课这种字符串特殊的课程用courseManipulating2()这个方法。所以也就对应不同的正则表达式。
一般的课程添加的方法解释(courseManipulating)
查看代码
private void courseManipulating(String[] sections, int ln) {
String courseName = sections[0];
String courseType = sections[1];
String courseAccessMode = null;
if (ln == 3) {
courseAccessMode = sections[2];
} else {
courseAccessMode = "考试";
}
if (null == Course.find(listCourses, courseName)) {
boolean examinationCourseWrong = "必修".equals(courseType)
&& ("考察".equals(courseAccessMode) || "实验".equals(courseAccessMode));
boolean inspectationCourseWrong = "选修".equals(courseType) && "实验".equals(courseAccessMode);
boolean experimentCourseWrong = "实验".equals(courseType)
&& ("考试".equals(courseAccessMode) || "考察".equals(courseAccessMode));
if (examinationCourseWrong || inspectationCourseWrong || experimentCourseWrong) {
System.out.println(courseName + " : course type & access mode mismatch");
} else {
Course course = new Course(courseName, courseType, courseAccessMode);
listCourses.add(course);
}
}
}
首先先处理一下用户输入的课程是以什么方式通过,是考试还是考察?,如果字符串只能被空格拆分成两个的话,课程的通过方式就一定要是考试(根据题目的意思)。
之后看到我们从listCourses课程列表根据课程名字中找到课程,如果找不到课程就说明这个以这个课程名字的课程没有被添加过,if条件为真,进行下面语句。
如果课程类型(必修 vs 选修)与课程通过方式(只能考试 vs 只能考试或者考察)不匹配的话,就输出错误,提示用户出错。如果课程类型与课程通过方式匹配的话,进行下面语句。
创建(new)一个课程(Course),并添加到课程列表(listCourses)。
实验课程添加的方法解释(courseManipulating2)
查看代码
private void courseManipulating2(String[] sections, int ln) {
String courseName = sections[0];
String courseType = sections[1];
String courseAccessMode = sections[2];
int experimentTimes = Integer.parseInt(sections[3]);
if (ln != experimentTimes + 4) {
System.out.println(courseName + " : number of scores does not match");
} else {
double importanceSum = 0;
Vector<Double> everyImportance = new Vector<>();
for (int i = 0; i < experimentTimes; i++) {
int index = i + experimentTimes;
double parseDouble = Double.parseDouble(sections[index]);
importanceSum += parseDouble;
everyImportance.add(parseDouble);
}
if (0 != Double.compare(importanceSum, 1.0)) {
System.out.println(courseName + " : weight value error");
} else {
Course course = new Course(courseName, courseType, courseAccessMode);
course.addExperimentImportance(everyImportance);
course.addExperimentTimes(experimentTimes);
if (!listCourses.contains(course)) {
listCourses.add(course);
}
}
}
}
首先实验课程的添加要根据实验的次数,对每一次实验赋予权重,所以一开始判断用户输入的实验次数,和给的权重个数是否匹配。如果匹配,进行下面语句。
每一个权重都记录到一个list列表里面(我这里用的是Vector向量),最后计算权重的总和。如果权重之和不等于1,就输出权重和不等于1,输出错误。如果权重和等于1,进行下面语句。
如果课程列表不存在这门课程就添加这么实验课,这样就不会出现重复的课程。
学生选课添加的方法解释(scoreManipulating,是除了实验课的学生选课)
查看代码
private void scoreManipulating(String[] sections, int ln) {
String studentId = sections[0];
String studentName = sections[1];
String studentCourse = sections[2];
String classNumber = studentId.substring(0, 6);
Student student = judgeStudentIsNew_andAddStudent(studentId, studentName, studentCourse, classNumber);
Course course = Course.find(listCourses, studentCourse);
String accessMode = course.getCourseAccessMode(listCourses, studentCourse);
if ("考试".equals(accessMode)) {
if (5 == ln) {
int usualPerformance = Integer.parseInt(sections[3]);
int finalGrade = Integer.parseInt(sections[4]);
if ((usualPerformance >= 0 && usualPerformance <= 100) || (finalGrade >= 0 && finalGrade <= 100)) {
Score score = new ExaminationScore(usualPerformance, finalGrade);
SelectCourse selectCourse = new SelectCourse(course, student, score);
add(selectCourse);
}
} else {
System.out.println(studentId + " " + studentName + " : access mode mismatch");
}
} else if ("考察".equals(accessMode)) {
if (4 == ln) {
int finalGrade = Integer.parseInt(sections[3]);
if (finalGrade <= 100 && finalGrade >= 0) {
Score score = new InspectationScore(finalGrade);
SelectCourse selectCourse = new SelectCourse(course, student, score);
add(selectCourse);
}
} else {
System.out.println(studentId + " " + studentName + " : access mode mismatch");
}
} else if (null == accessMode) {
System.out.println(studentCourse + " does not exist");
}
}
private Student judgeStudentIsNew_andAddStudent(String studentId, String studentName, String studentCourse,String classNumber) {
boolean studentEnterClass = (null != Student.find_fromClasses(listStudentClasses, studentId)) ? true : false;
boolean classExsitList = (null != StudentClass.find(listStudentClasses, classNumber)) ? true : false;
boolean studentExistList = (null != Student.find_fromStudents(listStudents, studentId)) ? true : false;
Student student = new Student(studentId, studentName, studentCourse);
judgeIsNew(classNumber, studentEnterClass, classExsitList, studentExistList, student);
return student;
}
private void judgeIsNew(String classNumber, boolean studentEnterClass, boolean classExsitList,boolean studentExistList, Student student) {
if (!classExsitList) {
StudentClass studentClass = new StudentClass(classNumber);
add(studentClass);
}
if (!studentExistList) {
add(student);
}
if (!studentEnterClass) {
StudentClass studentClass = StudentClass.find(listStudentClasses, classNumber);
studentClass.add(student);
}
}
学生选课,首先要判断这个学生是不是新的,有没有被添加到学生列表(listStudents)呢?所以要用到judgeStudentIsNew_andAddStudent()方法,而这个方法又会调用judgeIsNew()方法,所以下面两个自然段就来介绍这两个方法。
judgeStudentIsNew_andAddStudent()方法:首先判断这个学生有没有进入班级,即学生是否存在班级列表中(listStudentClasses),这个班级是否存在在班级列表中(listStudentClasses),学生是否存在学生列表中呢?最后进入JudgeIsNew()方法中。
judgeIsNew()方法:如果学生班级不存在在班级列表,就创建一个新的放进列表。如果学生不存在在学生列表,就创建一个新的放进列表。如果本来是在这个班级的学生不存在在对应的学生班级,就将学生放进对应的班级中(为什么能将学生放入学生班级呢?是因为学生班级[studentClass]里面有容器可以承装[Student])。
执行完judgeStudentIsNew_andAddStudent()方法后,进行下面语句。如果学生选的课的课程通过方式是考试的话,就解析成选课添加到选课列表(listSelectCourse)。如果学生选的课的课程通过方式是考察的话,就解析成选课添加到选课列表(listSelectCourse)。如果学生选的课的课程通过方式是空的话,就是没有根据课程名称找到对应的课程,输出这个课程不存在,让用户知道错误。
这就是除了实验课的选课的添加。
学生选课添加的方法解释(学生选的是实验课,experimentManipulating)
查看代码
private void experimentManipulating(String[] sections, int ln) {
String studentId = sections[0];
String studentName = sections[1];
String studentCourse = sections[2];
String classNumber = studentId.substring(0, 6);
Student student = judgeStudentIsNew_andAddStudent(studentId, studentName, studentCourse, classNumber);
Course course = Course.find(listCourses, studentCourse);
String courseAccessMode = Course.getCourseAccessMode(listCourses, studentCourse);
if (!"实验".equals(courseAccessMode)) {
System.out.println(studentCourse + " : course type & access mode mismatch");
return;
}
int expermentTimes = course.experimentTimes;
int neededLength = 3 + expermentTimes;
if (neededLength != ln) {
System.out.println(studentId + " " + studentName + " : access mode mismatch");
} else {
Vector<Integer> everyGrade = new Vector<>();
for (int i = 0; i < expermentTimes; i++) {
int idx = 3 + i;
int score = Integer.parseInt(sections[idx]);
everyGrade.add(score);
}
Vector<Double> everyImportance = course.everyImportance;
ExperimentScore experimentScore = new ExperimentScore(expermentTimes, everyGrade, everyImportance);
SelectCourse selectCourse = new SelectCourse(course, student, experimentScore);
listSelectCourses.add(selectCourse);
}
}
private Student judgeStudentIsNew_andAddStudent(String studentId, String studentName, String studentCourse,String classNumber) {
上文已经讲过了。
}
如果学生选的课程的课程通过方式不是实验,就输出课程类型和课程通过类型不匹配。如果匹配的话,进行下面语句。
如果学生实验分数的个数不等于实验次数的话,就输出学生课程通过不匹配。如果这个if语句通过,进行下面语句。
将每次实验分数读到,放入容器,后准备好应有的成员变量,组合成选课(selectCourse),放入选课列表(listSelectCourses)
解读代码三(parseInput类中的showStudents,showCourses,showClasses方法)
查看代码
public void showStudents() {
listStudents.sort((o1, o2) -> {
String sid1 = o1.studentId;
String sid2 = o2.studentId;
return sid1.compareTo(sid2);
});
for (Student ck : listStudents) {
Vector<Score> score = Student.findScore(listSelectCourses, ck);
if (0 == score.size()) {
String sid = ck.studentId;
String sname = ck.stuName;
System.out.println(sid + " " + sname + " did not take any exams");
} else {
Student.calculateScore(ck);
String sid = ck.studentId;
String sname = ck.stuName;
int sscore = ck.averageTotalScore;
System.out.println(sid + " " + sname + " " + sscore);
}
}
}
输出学生相关信息,需要对学生的学号(id)进行排序。学生的学号没有中文,只有数字,是字符串的类型。可以直接使用字符串的比较方法,就能学生按照学号升序排序。如果需要降序,直接加个负号加个括号就行。排序完后,执行下面代码。
将学生每个都取出来,在选课列表中找到这个学生有的选课,并把这个学生的每一个选课的成绩都取出来。放入成绩列表(score)。执行完这条语句,进行下面代码。
如果成绩列表的长度为0,就说明这个学生压根就没有成绩,所以输出这个学生没有考过任何一场考试。如果学生有成绩的话,进行下面代码。
对学生进行计算成绩,最后输出。因为一个学生可能有多门课,calculateScore就是来计算学生多门课最后的最终总期末成绩,并输出。
这样就完成了学生信息的输出。
查看代码
public void showCourses() {
Comparator<Object> CHINA_COMPARE = Collator.getInstance(java.util.Locale.CHINA);
listCourses.sort((o1, o2) -> ((Collator) CHINA_COMPARE).compare(o1.courseName, o2.courseName));
for (Course ck : listCourses) {
Vector<SelectCourse> need = new Vector<>();
String courseName = ck.courseName;
String courseAccessMode = ck.courseAccessMode;
Course.findNeed(ck, need, listSelectCourses);
if (0 == need.size()) {
System.out.println(courseName + " has no grades yet");
} else {
Course.calculateScore(ck, need);
if ("考察".equals(courseAccessMode)) {
InspectationScore score = (InspectationScore) ck.score;
int finalGradeAverage0 = score.finalGrade;
int totalGradeAverage0 = score.totalGrade;
System.out.println(courseName + " " + finalGradeAverage0 + " " + totalGradeAverage0);
} else if ("考试".equals(courseAccessMode)) {
ExaminationScore score = (ExaminationScore) ck.score;
int usualPerfomanceAverage0 = score.usualPerformance;
int finalGradeAverage0 = score.finalGrade;
int totalGradeAverage0 = score.totalGrade;
System.out.println(courseName + " " + usualPerfomanceAverage0 + " " + finalGradeAverage0 + " "
+ totalGradeAverage0);
} else if ("实验".equals(courseAccessMode)) {
ExperimentScore score = (ExperimentScore) ck.score;
int totalGradeAverage0 = score.totalGrade;
System.out.println(courseName + " " + totalGradeAverage0);
}
}
}
}
输出课程相关信息,需要对课程进行按拼音排序。因为对于
标签:String,选课,int,学生,BLOG,课程,PTA6,sections From: https://www.cnblogs.com/liujiantao/p/17507891.html