blog-1
前言
在过去的几周内,我们完成了题目集1至3的练习,涉及多个知识点和编程技巧。整体来说,这三次题目集共包含了12道题目,题目难度逐步增加。
题量:
题目集1(5道题),题目集2(4道题),题目集3(3道题)。
难度:
题目集1:稍微困难,适合刚入门的学习者,涵盖的知识点有类的定义、对象的创建、封装、构造方法的使用、数据访问、字符串处理以及方法重写等知识点,通过第一次题目集,可以更好地理解这些概念。
题目集2:中等难度,涉及较复杂的数据结构和基本算法,涵盖的知识点与第一次大致相同,但加强了对字符串处理的知识点的应用。
题目集3:较高难度,需要综合运用多种知识点,特别是最后一题,考验了对设计模式的规划,加强对数据结构映射的应用,以及输入输出处理难度加大。 通过这三次练习,我对编程的思维方式有了更深的理解,也提高了编写代码的能力。
设计与分析
题目集1 7-5 答题判题程序-1
设计
思路
类图
用户使用答题程序-1顺序图
分析
报表数据
从图中数据可以得知,除 Avg Depth稍稍偏高外其它各项基本正常。
题目类
1 class Question { 2 private int id; 3 private String content; 4 private String standardAnswer; 5 public Question(int id, String content, String standardAnswer) { 6 this.id = id; 7 this.content = content.trim(); 8 this.standardAnswer = standardAnswer.trim(); 9 } 10 public boolean checkAnswer(String answer) { 11 String a=answer.trim(); 12 return standardAnswer.equals(a); 13 } 14 }
每个题目都有相应的题目Id、题目内容以及题目标准答案,所以我们在题目类中设计三个私有数据分别存储题目Id、题目内容以及题目标准答案,同时我们应该使用有参构造函数的方法来存储每个题目。由于对于答卷中的每个题目答案我们都需要将其与相应题目的标准答案进行比较,所以我们可以直接在题目类中设计一个 boolean 类型checkAnswer 方法进行返回判断结果,快速得到结果。
试卷类
1 class Exam { 2 private List<Question> questions; 3 public Exam() { 4 this.questions = new ArrayList<>(); 5 } 6 public void addQuestion(Question question) { 7 questions.add(question); 8 } 9 public List<Question> getQuestions() { 10 return questions; 11 } 12 public Map<Integer, Question> getQuestionMap() { 13 Map<Integer, Question> map = new HashMap<>(); 14 for (int i = 0; i < questions.size(); i++) { 15 Question q = questions.get(i); 16 map.put(q.getId(), q); 17 } 18 return map; 19 } 20 }
每一张试卷都有引用题目类中的若干已存储的题目信息,由于这道题没有要求我们判断引用的题目是否存在题目类中,而且我们事先并不知道每张试卷都分别有哪些题目,所以我们可以使用 ArrayList 动态数组对试卷中题目信息进行存储,然后我们可以通过获取 ArrayList 的长度从而获得题目数量信息。我们在getQuestionMap()方法中使用了 Map接口和 HashMap<>() 实现,用于存储问题 ID 与问题对象之间的映射关系,public Exam()这是一个无参构造函数,用于初始化 Exam 类的实例,并在构造时创建一个新的ArrayList对象,最后使用List<Question>和 Map<Integer, Question> 的返回类型,允许调用者获取类内部的集合数据。
答卷类
每个题目对应的答案都是在一张试卷上作答,所以我们在答卷中设计一个试卷类型的属性对相应试卷信息进行存储,同时对于试卷中的每个题目,我们需要知道其答卷中的作答,对于每个作答我们在判断其是否正确,所以我们又设计了创建新的 ArrayList
对象用于 answers
和 results
将答案以及结果进行存储,并且我们使用了 ArrayList 便于动态更新这些信息。我们在答卷类中设计了一个evaluateAnswers方法,通过其调用试卷类中方法获取题目以及用户的答案信息,然后再调用题目类中判断方法,对每个答案进行判断,并将判断结果保存到结果中。由于我们最后输出的信息与每张答卷有关,所以我们在答卷类中设计了printResults(Map<Integer, Question> questionMap)方法,打印每个问题的内容和用户的回答,同时打印每个答案的正确性结果。最后只需要在测试类中调用这个方法对每张答卷最后的结果进行打印。AnswerSheet(Exam exam)构造函数接收一个 Exam
对象并初始化 AnswerSheet
的实例,同时创建新的 ArrayList
对象用于 answers
和 results。
测试类
测试类用于数据读取,由于题目,答卷类是输入一行的一大段字符串来代表题目的信息,所以我们应该通过正则表达式来将字符串分解,将#N,#A中的信息提取到对应存储空间中进行存储。并且调用答卷中的方法将结果打印出来。
题目集2 7-4 答题判题程序-2
设计
思路
题目集2相对于题目集1在试卷信息中增加了分数,以及多张试卷的信息,因此我们需要在试卷类中增加相应的属性对分数以及对试卷的Id进行存储,同时设计合适的方法对分数进行判断,同时题目输入的数据每行对应不同信息,每行开头使用不同的字符串进行各类信息的区分输入,所以我们可以新加一个QuizSystem类用于处理各类信息存储,并且将各类信息字符串进行处理,打印最后的结果。
类图
用户使用答题程序-2顺序图
分析
数据报表
从图中数据可以得知,除 Max Complexity偏高外其它各项基本正常。
题目类
此次作业中题目类与第一次作业题目类基本相同,我将判断方法封装进了新加的QuizSystem类。
试卷类
相较于第一次作业,这次作业中试卷中每个题目都增加了分数信息,以及试卷Id,并且相同的题目在不同的试卷中其分数是不相同的,所以我们在试卷类中新增一个分数属性用于存储试卷中对应题目的分数信息,和一个试卷Id信息用于存储对应的试卷信息。由于对于每张试卷我们都需要判断其总分是否是 100 分,所以我增加了一个addQuestion()方法计算每张试卷的总分。由于我对动态数组运用不够熟练以及总是报错,于是我将第一次作业中试卷类方法全盘改造,从自己会的入手,有时间将会尝试用之前的方法做出来。
答卷类
与上个改造原因相同,这次只简单的在类中加入一个构造方法,没有事先规划好类中的方法。
答卷系统类
答卷系统类中,我们首先使用processInput()方法将开头信息进行比对后,将对应信息用对应方法进行处理。在每个信息处理方法中,我们使用不同的正则表达式进行分割字符串,然后将对应信息存入相应的类中。最后我们使用generateOutput()方法将输出信息一一打印出来,在generateOutput()方法中,我们需要判断试卷是否为满分,试卷是否存在,答卷中用户是否给出答案,以及将总分和各题得分情况打印出来。由于时间的原因,这个类的设计没有做到很好,方法颇为臃肿,后续我将会减少该类的方法,将对应方法封装到各自的类中去。
测试类
在测试类中,输入测试数据,只需调用答卷系统中的方法就能实现程序所需完成的任务,减轻了测试难度。
题目集3 7-3 答题判题程序-3
设计
思路
这次作业相较于题目集2增加了学生姓名、学号,因此我们应该再设计一个类存储这些信息,同时在答卷系统类中还需要添加一个处理删除题目信息的方法,并且需要知道某个题目是否被删除,因此我们还需要设计一个状态属性来存储被删除题目的标识。这次题目集要求我们规范输入格式,因此我们可以使用正则表达式来判断输入,使我们的程序可读性更高。
类图
用户使用答题程序-3顺序图
分析
报表数据
从图中数据可以得知,各项数据都有点不在正常范围内,说明此代码需要进行改进,在后续作业中我会加强对这些的注意。
题目类
在题目类中,为了能够尽快查找某个题目是否被删除,所以我增加了一个状态属性,用于判断该题目是否被删除,以及增加了题目编号这一属性用于判断对应题目在试卷类中对应答案是否正确。
试卷类
在试卷类中,我新增加了一个属性,用于题目数量的记录,因为后续的字符串拆分过程中我们需要用到这个属性来判断题目数量。
答卷类
与上次作业中的设计大致相同,由于对于每个学号我们都需要找到其对应的学生信息,所以增加了一个新属性,记录答卷人的信息,用于将学号名字打印出来。
答卷系统类
在答卷系统类中,处理信息的方法中加入了正则表达式字符串匹配过程,这应该写入一个方法中,但我却没有写入,导致有点像面向过程。
学生类
1 class Student { 2 String id; 3 String name; 4 5 public Student(String id, String name) { 6 this.id = id; 7 this.name = name; 8 } 9 }
学生类包含基本属性,姓名与学号,以及有参构造函数的方法。
踩坑心得
坑1:题目可以有空格
在第二次作业中,由于没把第一次作业中题目中可以有空格这个隐藏坑点记住导致我答题程序-2有个测试点总是过不了
例如:
输入
#N:1 #Q:1+ 1= #A:2
#N:2 #Q:2+2= #A:4
#T:1 1-5 2-8
#S:1 #A:5 #A:22
end
将题目中的答案给去除了。后面请教老师,给我一个输出数据让我找自己的错误,看到那个空格,如梦初醒。
坑2:答案可以有空格
在答题程序-3中,允许答案中有空格。开始没有考虑到这个问题,导致输出的答案错误。
例如:
输入
#N:1 #Q:1+1= #A 2
#N:2 #Q:1+1= #A:2 #B:3
#T:1 1-5 2-2 3-9
#X:20201103 Tom-20201104 Jack
#S:1 20201103 #A:1-5 #A:2-2 3
#D:N-2
end
显示的为错误的输入,后面重新将正则表达式改为 "^#S:\\w+(\\s+\\w+)?(\\s+(#A:.+$))*?$"即可解决问题。
坑3:
由于自己的疏忽,将人名查找时总是只有一个人,其他人都显示为20201105 not found,由于给出的测试点中没有给出查找其他人的测试点,导致我始终没有找到我的错误。后面发现自己的代码中查找学生姓名时始终只查找第一个人。
这个坑纯属于自己给自己挖坑,自己却没有发现。后面增加一个属性记录学生数量,查找次数为学生数时,问题得以解决。
通过上述三个坑点,我了解到在处理复杂输入时,正则表达式是一种强大的工具,可以帮助我们验证数据格式。然而,设计正则表达式时需谨慎,因为不准确的模式可能导致误判,将有效输入识别为错误。这不仅会影响用户体验,还可能导致数据丢失或错误分析。因此,确保正则表达式的准确性和健壮性至关重要。可以通过测试用例和边界条件反复验证,确保其在各种输入情况下都能正常工作,从而提升数据的可靠性和系统的整体健壮性,以及要对循环次数的把控要准确,做题要有连贯性,不能断断续续的做题。
改进建议
自己的类中,有些类设计的方法过于繁多,这都是面向过程的思维方式,后续会将各自类中的方法封装完善,使每个类做好自己的工作。还有类中的方法中有些代码过于庞大,应该细化到其他方法中去。在接收到答卷信息时先将接收到的信息单独存储起来而不进行处理,在最后输出答卷信息时我们再统一处理,我们就可以按照正确的格式进行输出,避免输入时顺序打乱而程序崩溃。
总结
在本阶段的三次作业中,我深刻认识到了编程能力的提升以及面对对象编程的重要性。每次作业的题目数量虽然在减少,但难度却逐渐加大,这让我体会到了编程学习的渐进性和挑战性。在前几道题中,我们可以通过简单地创建几个类迅速完成,但最后一题往往需要我们综合运用所学知识,真正考验我们的代码能力。随着作业的深入,代码的复杂性和功能需求也在逐渐增加。这种变化促使我在编程时必须更加注重类的设计和功能划分,合理的类结构不仅可以提高代码的可读性和可维护性,还能实现代码的复用,这正是面对对象编程的核心优势。通过这三次作业,我不仅提高了自己的编程能力,更加深刻地理解了面对对象程序设计的价值。同时,庞大的代码量和复杂的输入形式也促使我在思维方式上更加灵活,能够更好地应对问题。然而,我也意识到自己在正则表达式的应用上还有很大的进步空间。尽管正则表达式极为便利且功能丰富,但在实际使用中,我发现自己对其掌握并不够充分。在编写正则表达式时,我经常需要反复修改,导致时间的浪费。这让我明白,只有通过深入学习和实践,才能真正掌握这一强大的工具。因此,在接下来的学习中,我将更加重视正则表达式的研究,努力提高自己的能力。对于教师、课程和作业的改进建议,我认为可以增加一些针对正则表达式的专项练习,以帮助学生更好地理解和运用。此外,在课程组织上,可以引入更多的实践案例,让学生在解决实际问题中提升编程技能。通过这次阶段性的总结,我对未来的学习方向有了更清晰的认识,希望在今后的学习中能不断提升自己的编程能力,掌握更多实用的知识与技能。