第二次博客作业
一、 前言
在过去的几周里,通过4~6三组题目集的深入练习,在巩固前三次题目集知识的基础上,我又学习并掌握了更多的知识,比如继承与多态的运用、比较器Comparator的运用等等。一次次练习不仅加强了我的基础编程技巧,还逐步引入了更为复杂的数据结构和算法应用,使我的编程能力和问题解决技巧在作业迭代中得到了显著提升。
题目集4 共有3道题:主要涉及类与对象设计与创建,还有继承与多态的一些运用,进一步完善了答题判题程序,较为容易。
题目集5 共有3道题:与之前的答题程序不同,这次是关于家居强电电路模拟程序的编写,涉及到了类与多态的运用,大量使用了继承方面的知识,刚开始时没有头绪,后来对程序的整体架构进行了全面的设计与分析,逐渐有了思路。
题目集6 只有1道题目,它是题目集5第1题的进阶版,没有小题。这道题目增加了落地扇、并联电路等复杂信息,对我来说是一个较大的挑战,但也是提升我编程技能的好机会。
二、 设计与分析
大作业4
第1题是以给出的Main类为基础,构建各个角色类,将代码补充完整,较为简单。用于锻炼类与对象的设计与创建的能力,还涉及到了继承,子类(派生类)可继承父类(基类)的属性和方法,Faculty 和 Staff 类中的 show 方法使用 super.show() 调用父类 Employee 的 show 方法,就是一种多态的体现。
第2题是设计一个学生类和它的一个子类——本科生类,同样涉及到了继承的使用。
第3题是前三次答题判题程序的进一步拓展,新增了选择题、多选题、填空题等,且判断题目正确与否计算分数时,除了true、false,还增加一项”partially correct”表示部分正确,也可得分。
新增类:
点击查看代码
class MultipleChoiceQuestion extends Question {
public MultipleChoiceQuestion(String info) {
super("Z", info);
}
public boolean checkAnswer(String answer) {
String[] correctAnswers = this.answer.split(" ");
String[] userAnswers = answer.split(" ");
Set<String> correctSet = new HashSet<>(Arrays.asList(correctAnswers));
Set<String> userSet = new HashSet<>(Arrays.asList(userAnswers));
return correctSet.equals(userSet);
}
public boolean isPartiallyCorrect(String answer) {
String[] correctAnswers = this.answer.split(" ");
String[] userAnswers = answer.split(" ");
Set<String> correctSet = new HashSet<>(Arrays.asList(correctAnswers));
Set<String> userSet = new HashSet<>(Arrays.asList(userAnswers));
return !userSet.isEmpty() && !correctSet.containsAll(userSet) && userSet.containsAll(correctSet);
}
public int calculatePartialScore(int fullScore) {
if (checkAnswer(this.answer)) {
return fullScore; // 完全正确,得满分
} else if (isPartiallyCorrect(this.answer)) {
return fullScore / 2; // 部分正确,得一半分
} else {
return 0; // 完全错误或包含错误答案,得0分
}
}
}
class FillInTheBlankQuestion extends Question {
public FillInTheBlankQuestion(String info) {
super("K", info);
}
public boolean checkAnswer(String answer) {
return this.answer.equals(answer);
}
public boolean isPartiallyCorrect(String answer) {
String[] correctAnswers = this.answer.split("或");
for (String correctAnswer : correctAnswers) {
if (correctAnswer.trim().equals(answer)) {
return true;
}
}
return false;
}
}
本题的设计思路是实现一个在线考试系统,用户根据问题输入答案后提交答案,系统会验证答案的正确性并给出分数。系统由三个主要的类组成:Question、Paper和Answer,以及一个ExamSystem类来管理整个考试流程。
1. Question类:表示单个问题,包含问题ID、问题内容和正确答案。它还包含一个方法来检查给定的答案是否正确。Question类是基类,还有两个子类MultipleChoiceQuestion和FillInTheBlankQuestion,分别表示选择题和填空题,它们实现了不同的答案检查逻辑,在这两个类中新增了isPartiallyCorrect(String answer):检查答案是否部分正确。这个方法将正确答案和用户答案转换为集合,然后检查用户答案集合是否完全包含于正确答案集合中,但不是完全相同,这种情况被视为部分正确。calculatePartialScore(int fullScore):根据答案的完全正确或部分正确情况计算得分。如果完全正确,返回满分;如果部分正确,返回一半分数;如果完全错误,返回0分。
2. Paper类:代表一套试卷,用于管理多个Question对象。它提供了添加问题的方法和获取排序后的问题列表的方法。Paper类还包含了计算分数的方法,能够根据用户的答案计算总分和每题的得分。
3. Answer类:用于收集用户的答案并验证答案的正确性。它存储了答案和对应的结果,并提供了一个方法来打印问题和用户的答案以及验证结果。
4. ExamSystem类:是整个系统的管理类,负责添加问题、删除问题、添加试卷、添加学生、处理答案和处理所有答案。它包含了所有问题、试卷、学生和学生答案的映射,以及相关的方法来操作这些数据。
运行流程:
• 通过Scanner类读取用户输入的指令,包括问题添加、试卷添加、学生添加和答案提交。
• 当读取到问题添加指令时,根据问题类型创建相应的Question对象,并将其加入到ExamSystem的allQuestions映射中。
• 当读取到试卷添加指令时,创建Paper对象,并根据指令中的信息添加问题到试卷中,然后将试卷加入到ExamSystem的allPapers映射中。
• 当读取到学生添加指令时,将学生信息加入到ExamSystem的allStudents映射中。
• 当读取到答案提交指令时,根据试卷ID和学生ID,将答案存储在ExamSystem的studentAnswers映射中。
• 使用processAllAnswers方法来处理所有学生的答案,验证每个答案的正确性,并计算总分。
• 最后,使用print方法输出每个问题的题目、用户的答案以及答案的正确与否,以及总分。
整体上,代码可实现试卷和问题的灵活管理,允许动态添加问题和试卷,可实现答案的收集和校验,还提供了基本的测试评分功能,在前三次的基础上增加了部分正确得分。通过面向对象的方法将考试系统的各个组成部分进行了有效的封装和模块化,使得每个类的职责明确,易于理解和维护。然而,代码仍不完善,需要进一步的测试和优化,以确保在各种输入情况下都能正确运行。
SourceMonitor图表如下
由左图可以看到:代码的平均深度、最大复杂度较高,仍有待改进提升,且代码的注释较少
大作业5
第1题不再是答题程序,而是家居强电电路模拟程序,因此在编写第五次题目集之前,先对整体的程序架构进行了一个完整的设计
本题类图与时序图如下
本题的设计思路是实现一个模拟电路系统,它能够让用户定义电路组件之间的连接,并通过控制信号来调节电路的行为,最终展示每个组件的状态。系统由多个主要的类组成,包括CircuitDevice、Switch、Fendang、Lianxu、Baichi、Riguang和Diaoshan,以及一个Main类来管理整个电路的构建和操作流程。
1. CircuitDevice类:表示电路中的一个基本设备,包含设备ID和电压属性。它是所有电路设备的基类,提供了更新电压和显示设备状态的抽象方法。
2. Switch类:表示一个开关设备,继承自CircuitDevice。它能够在打开和关闭之间切换,根据开关状态更新电压,开关状态为0时,无论输入电位是多少,输出引脚电位为0。当开关状态为1时,输出引脚电位等于输入电位。
3. Fendang类:表示一个分档设备,继承自CircuitDevice。它能够根据速度档位调节电压,档位值从0档-2(3/4)档变化,每个档位的输出电位分别为0、0.3、0.6、0.9倍的输入电压。
4. Lianxu类:表示一个连续调节设备,继承自CircuitDevice。它能够根据给定的线性值调节电压,没有固定档位,按位置比例得到档位参数,数值范围在[0.00-1.00]之间,含两位小数。输出电位为档位参数乘以输入电压。
5. Baichi类:表示一个白炽灯设备,继承自CircuitDevice。它根据电压显示亮度,亮度在0~200lux(流明)之间。电位差为0-9V时亮度为0,其他电位差按比例,电位差10V对应50ux,220V对应200lux,其他电位差与对应亮度值成正比。白炽灯超过220V。
6. Riguang类:表示一个日光灯设备,继承自CircuitDevice。它根据电压显示亮度,只有两种状态,电位差为0时,亮度为0,电位差不为0,亮度为180。
7. Diaoshan类:表示一个吊扇设备,继承自CircuitDevice。它根据电压显示转速,输入输出电位差小于80V时转速为0,超过150V转速为360转/分钟,其他电压值与转速成正比n=(voltage - 80) * 4 + 80。
8. Main类:是整个电路系统的管理类,负责读取用户输入的电路连接和控制指令,构建电路,调节电路设备,并最终输出每个设备的状态。
运行流程:
- 通过Scanner类读取用户输入的电路连接和控制指令,输入一共分为两种
第一种:连接信息"["+引脚号+" "+...+" "+引脚号+"]",例如[K1-1 K3-2]表示K1的1引脚,K3的2引脚连接在一起。
第二种:控制设备调节信息
开关:#+设备标识K+设备编号,例如:#K2,代表切换K2开关的状态。
分档调速器:#+设备标识F+设备编号+"+" 代表加一档,例如:#F3+,代表F3输出加一档。
#+设备标识F+设备编号+"-" 代表减一档,例如:#F1-,代表F1输出减一档。
连续调速器:#+设备标识L+设备编号+":" +数值 代表将连续调速器的档位设置到对应数值,例如:#L3:0.6,代表L3输出档位参数0.6。 - 根据输入中的设备信息创建相应的电路设备对象,并将其加入到map映射中。
- 当读取到控制指令时,根据指令类型(如开关操作、分档调节等)对相应的设备进行操作,并更新电压。
- 遍历map映射中的所有设备,根据设备的电压和特定逻辑计算设备的状态。
- 使用display方法输出每个设备的ID和当前状态,如开关的开闭状态、分档的速度、白炽灯的亮度等。
- 当读取到“end”时,结束程序
SourceMonitor图表如下
由左图可以看到:代码的平均深度、平均复杂度和最大复杂度较高,仍有待改进提升,且代码的注释较少
第2题是阅读给出的程序并调试改正程序,以获得正确答案,锻炼了纠错能力。
大作业6
只有1道题,没有小题,是题目集5第1题的迭代加强版,增加了落地扇、并联电路等信息,对我来说难度较大,虽说目前还没做出来,但思路较清晰,后续一定完善
设计类图如下:
后续改进思路:
- 计算电阻和电压分配
根据电路的连接方式,计算并联电路的总电阻a和串联电路的总电阻b,然后根据电阻计算并联电路的电压和串联电路的电压。 - 构建电路映射
使用Map结构存储电路设备,便于管理和访问。分别遍历connection列表,将串联和并联电路的信息存储在映射中。
SourceMonitor图表如下
由左图可以看到:代码质量还行,但最大复杂度较高,仍有待改进提升
三、踩坑心得
在提交源码的过程中,我遇到了以下问题:
1.在使用分档调速器时,D2转速始终是360
未正确按照要求执行:每个档位的输出电位分别为0、0.3、0.6、0.9倍的输入电压,对代码做出以下修改后成功实现:
else if(sid.startsWith("F")){
String diu = sid.substring(0, 2);
String vs = sid.substring(2);
Electric electric = map1.get(diu);
if (electric != null) {
electric.regulate(vs);
flag2++;
if(vs.equals("+")){
flag++;
if(flag>=3){
flag=3;
}
}
if(vs.equals("-")){
flag--;
if(flag<=0){
flag=0;
}
}
}
else continue;
}
if(flag!=0){
rui=0.3*flag*rui;
}
if(flag==0&&flag2!=0){
rui=0;
}
2.输出顺序问题
输出未按标准顺序输出
为了确保在显示设备状态之前,设备是按照一定的顺序排列的:控制设备先输出,受控设备后输出,且同种用电器按顺序输出,通过查阅资料,以下代码实现了对CircuitDevice对象的集合进行排序,并根据排序结果显示每个设备的状态,具体步骤如下:
- 定义比较器(Comparator):
创建了一个匿名内部类Comparator,用于定义CircuitDevice对象的比较逻辑。在compare方法中,首先根据设备类型给每个设备一个优先级分数(a和b),例如,Switch的优先级最高为6,Diaoshan的优先级最低为1。
如果两个设备的类型优先级不同,比较器将根据这些优先级分数来确定顺序。
如果两个设备的类型优先级相同(a == b),则比较器将进一步根据设备的ID(c和d)来确定顺序。 - 排序集合:
使用Collections.sort(arraylist6, numberComparator)方法,根据上面定义的比较器对arraylist6中的CircuitDevice对象进行排序。 - 显示设备状态:
遍历排序后的arraylist6集合,调用每个设备的display方法来显示其状态。
改正后可正确输出:
四、改进建议
由上述SourceMonitor生成的报表可以看出代码普遍存在平均深度、平均复杂度和最大复杂度较高,代码注释少的问题,以下是改进建议:
1. 要降低代码复杂度
i. 重构代码:将复杂的代码分解成更小、更易于管理的函数或方法。每个函数应该只做一件事情,并且做好一件事情。识别代码中的公共模式和逻辑,将它们抽象成独立的模块或类。这有助于减少重复代码,提高代码复用性。
ii. 减少嵌套:优化循环和迭代,减少条件语句和循环的嵌套层数,利用多态减少条件分支,还可以通过提取方法、使用早返回(early return)等技巧来实现。
iii. 单一职责原则:确保每个类和方法都有一个明确的职责,并且只为这个职责服务。这有助于降低代码的耦合度,提高代码的可读性和可维护性。
2. 要提高代码可读性
i. 增加注释:在代码中添加必要的注释,解释复杂的逻辑、重要的决策点和代码的预期行为。注释应该简洁明了,能够为以后改进完善代码时提供足够的信息。
ii. 命名要规范:使用有意义的变量名和方法名,避免使用缩写和模糊的命名,这样即使没有注释,代码的意图也应该是清晰的。
五、总结
在过去的几次编程实践中,通过完成答题判题程序4以及两次家居强电电路模拟程序,我深刻体会到了Java编程的挑战性和实用性。虽然在这些项目中遇到了不少难题,分数也未能达到预期,但我在解题和后续优化代码的过程中收获颇丰。在答题判题程序4中,通过更进一步的迭代,对答题判题程序也有了更好的完善,加深了我对输入输出操作和异常处理方面的能力。在家居强电电路模拟程序的开发中,我不仅提升了对Java编程的掌握,还对电路模拟有了初步的认识。通过这个项目,我对正则表达式的运用有了更深刻的理解,学会了如何利用正则表达式解析复杂的电路配置。同时,我对ArrayList、HashMap等数据结构的运用也更加熟练,这些数据结构在处理电路元件和状态时显得尤为重要。面向对象设计方面,我更加熟悉了如何定义类、创建对象以及如何通过实例方法和类方法来实现功能,对封装、继承和多态等核心概念有了更深入的认识。
这些项目经验让我在编写和理解代码时更加得心应手,我意识到了代码的可读性和可维护性的重要性,并学会了如何编写更清晰、更模块化的代码,这为后续优化代码时理解源代码提供了极大帮助。同时,我在实践中不断将java的七个原则运用巩固,在未来的学习中,我希望我能有更强的动手能力,在实践中不断应用所学的知识,以逐步提高编程技能和解决问题的能力。我期待在未来的项目中,能够将这些经验转化为更高质量的代码和更高效的解决方案。