一、前言
该系列题目均为设计与实现一个小型的答题判题程序,题目难度从简单到复杂逐步提升,题目数量和输入信息也逐步增加。以下是对这三道题的总结:
1. 知识点
-
第4次题目集在第3次题目集的基础上又增加了难度,该题目集主要考察编程技巧和算法设计,特别是在答题系统中如何通过输入的数据来判断是否符合给定的标准和规则。判题逻辑的实现,包括如何解析输入数据、处理各种异常情况,并进行答题结果的判断。提高了代码的效率和稳定性,要求更高的编程能力。
-
第5次题目集主要涉及电路设计与模拟。
重点考察:
开关控制:如何通过开关来控制设备的开闭状态。
电路连接:包括串联与并联电路的基本理解。
设备状态:根据输入电压来确定设备(如灯、风扇等)的状态或参数。
基本的电路元件模型,包括开关、风扇、灯具等。 -
第6次题目集:相比“7-1 家居强电电路模拟程序-1”,这一题目集在原有基础上增加了更加复杂的功能,涉及更多的设备和更复杂的电路配置。
具体知识点包括:
分档调速器和连续调速器:如何根据输入调节设备的状态,尤其是调速器的档位调整。
并联电路的引入:如何处理并联电路中的设备控制和电流分配。
对电路设备的状态管理更加精细,设备的输入输出变化要求更加精确。
物理电路计算,包括电压、电流、电阻等方面的知识。
2. 题量
- 题目4:题量较多,问题的难度更多在于逻辑的判断和多种输入的处理。
- 题目5:题量相对较少,主要是一些基础设备的控制和简单电路的连接问题。
- 题目6:增加了更多的设备种类和复杂的电路设计,题量有所增加。
3. 难度
- 题目4:最具挑战性,特别是在输入数据的解析和判题逻辑的设计上,考察的重点是算法和编程技巧。
- 题目5:最为简单,重点考察基础电路原理和基本编程能力。
- 题目6:难度适中,加入了调速器和并联电路等复杂电路的设计,要求较强的电路分析能力。
二、设计与分析
第四题
该程序实现了一套完整的考试系统,从题库管理、试卷设计、学生信息录入,到答题处理和成绩计算,具备较强的实际操作价值。以下是对源码的详细分析,包括类设计、功能实现、数据流向等,同时配有类图与分析心得。
1. 类设计
- wt(抽象类):题目基类,定义了抽象方法 cj(评分) 和 sc(结果输出)。
- xzt (单选题):实现评分与结果输出逻辑,依据答案完全匹配或部分匹配给分。
- dx (多选题):实现多选题评分,支持全对、部分对、全错的情况。
- sj (试卷类):维护试卷中的题目及分值分布,确保总分为 100。
- Student类:存储学生信息(学号、姓名)。
- da (答题卡):存储学生在试卷上的答题记录。
- Main类:主程序,处理输入并调用其他模块完成题库管理、答题解析与成绩输出。
2. 主程序逻辑分析
-
题库管理:使用正则表达式解析输入数据 (#N 单选题, #Z 多选题)。questions 存储所有题目对象。
-
试卷设计:解析 #T 输入,分配题目与分值。检查试卷总分是否为 100 分。
-
答题管理:使用正则匹配解析学生答案 (#S)。存储在 answerSheets 中。
-
评分流程:遍历 answerSheets 中的每个学生答卷。对每题评分,累计总分,并输出详细结果。
-
核心代码:
if (testPaper == null) { // 校验试卷是否存在System.out.println("alert: test paper " + sheet.hao + " not found");return;}double zf = 0; // 总分List<String> results = new ArrayList<>();for (Map.Entry<Integer, String> entry : sheet.getAnswers().entrySet()) {int qId = entry.getKey(); // 当前题目IDwt question = questions.get(qId); // 获取题目对象if (question == null || !testPaper.getQuestions().contains(qId)) {System.out.println("the question " + qId + " invalid~0");results.add("0");continue;}// 评分double f = question.cj(entry.getValue()) * testPaper.getScore(qId);zf += f; // 累加分数System.out.println(question.sc(entry.getValue(), f)); // 输出答案评价results.add(String.valueOf((int) f));}// 输出学生成绩System.out.println(student.id + " " + student.mz + ": " + String.join(" ", results) + "~" + (int) zf);
3. 设计类图(使用PowerDesigner)
报表
指标 | 值 |
---|---|
总行数(LOC) | 211行 |
类数量 | 7 |
方法数量 | 19 |
最大圈复杂度 | 7 |
平均每行代码的字母数 | 41 |
4. 心得总结
- 面向对象原则实践:抽象基类(wt)+多态(xzt & dx)是符合开闭原则的设计,便于后续新增题型。
- 数据流完整性:通过多级映射(questions、testPapers、answerSheets),维护各模块数据之间的关系。
- 输入解析设计:借助正则表达式解决复杂数据的处理,极大提高了代码的通用性与灵活性。
第五题
代码实现的核心目标是模拟多个类型的电气设备(如开关、调速器、灯泡、吊扇等),通过命令对设备进行控制并模拟其响应(电压、电流、亮度、速度等)。
1. 类分析
-
Device 类:
定义了设备的通用属性(如 id、type、pin1、pin2 等)和行为(如 getOutputVoltage())。
为后续子类提供了统一接口。 -
ControlDevice 和 ControlledDevice:
ControlDevice:用于实现带输入控制功能的设备(如开关和调速器)。
ControlledDevice:用于模拟电压差和其对设备的影响(如灯泡亮度、吊扇速度)。 -
SwitchDevice、MultiSpeedController、ContinuousController:
实现了具体的设备逻辑(如开关的开闭状态、调速器的档位变化等)。 -
WhiteBulb、DaylightBulb 和 Fan:
根据电压差计算设备参数(亮度或转速)。
例如白炽灯的亮度是电压差的线性函数,而吊扇的转速与电压差之间有复杂的关系。 -
UnionFind 类:
并查集实现,管理引脚之间的连接关系。
通过路径压缩和合并优化,支持高效的连接查询操作。 -
Main类:
主类,处理用户输入、解析命令、初始化设备和计算电压等核心逻辑。
2. 类图设计(使用PowerDesigner)
使用PowerDesigner生成的类图如下:
报表
以下是针对该代码的 SourceMonitor 报表分析:
指标 | 值 |
---|---|
总行数(LOC) | 626 行 |
方法数量 | 25 |
类的数量 | 8 |
平均方法复杂度 | 8 |
最大方法复杂度 | 8 |
3. 心得总结
这个系统实现了电气设备的基本控制和模拟,采用了并查集管理引脚连接,使用BFS传播电压,并根据电压差来控制设备的状态。通过面向对象的设计和继承,使得系统的扩展性和可维护性得到了保证。
第六题
实现了一个基于设备控制的模拟系统,核心思想是通过并查集管理设备间的引脚连接,模拟设备之间的电压变化,并通过输入的控制命令来调整设备的状态。代码分为几个主要部分:设备管理、并查集的实现、电压计算和设备控制。
1.类设计:
- SwitchDevice (K):
职责:表示开关设备,能够在开启和关闭之间切换。
方法:toggle() 切换开关的状态,getOutputVoltage() 根据开关的状态计算电压输出。
设计思路:继承自 ControlDevice,实现了开关特有的行为。 - MultiSpeedController (F):
职责:表示分档调速器设备,可以在不同档位之间切换。
方法:increaseGear() 和 decreaseGear() 用于改变档位,getOutputVoltage() 根据当前档位计算输出电压。
设计思路:继承自 ControlDevice,实现了分档调速器的行为。 - ContinuousController (L):
职责:表示连续调速器,能够通过设置一个连续的参数来控制输出电压。
方法:setParameter() 用于设置档位参数,getOutputVoltage() 根据当前档位参数计算输出电压。
设计思路:继承自 ControlDevice,实现了连续调速的行为。 - WhiteBulb (B) 和 DaylightBulb (R):
职责:这些类表示两种类型的灯泡,分别是白炽灯和日光灯。它们根据电压差计算亮度。
方法:computeBrightness() 计算亮度,getStatus() 返回亮度值。
设计思路:继承自 ControlledDevice,实现了根据电压差计算亮度的行为。 - Fan (D):
职责:表示风扇,根据电压差计算转速。
方法:computeSpeed() 计算转速,getStatus() 返回转速。
设计思路:继承自 ControlledDevice,实现了根据电压差计算风扇转速的行为。
2. 类图设计(PowerDesigner)
以下是基于当前代码的类图,展示了各个类的属性和方法。
类图说明
- Device 类:是所有设备类的基类,包含所有设备共享的属性和方法。
- ControlDevice 类 和 ControlledDevice 类:分别为控制设备和受控设备的基类。控制设备如 SwitchDevice、- MultiSpeedController、ContinuousController 等,受控设备如 WhiteBulb、Fan 等,都继承了相应的基类。
- 并查集类 UnionFind:用于管理引脚的连接,提供了 find() 和 union() 方法,确保电压差的计算正确。
SourceMonitor 报表分析
以下是针对该代码的 SourceMonitor 报表分析示例:
指标 | 值 |
---|---|
总行数(LOC) | 627行 |
方法数量 | 34 |
类的数量 | 11 |
平均复杂性 | 较高 |
注释比例 | 15% |
顺序图
3. 心得
这个程序要完全实现对于我而言比较困难,虽然类图做出来了,有了基本框架,但是在实现整个功能的时候,还是会遇到很多问题,在上一次代码的基础上增加了部分功能,但是复杂度也提升了很多。
三、采坑心得
第四次作业
- 输入格式问题:
在TEST_PAPER_PATTERN和ANSWER_PATTERN的匹配中,输入数据需要严格按照指定格式给出,尤其是空格、分隔符等,稍有不匹配就会导致无法正确解析。确保输入的每行数据符合预期格式非常重要。 - 部分匹配逻辑的处理:
对于单选题和多选题的部分正确情况,代码通过判断contains来处理,但实际使用中可能会遇到学生答案与正确答案部分重合的情况,需要保证cj()方法的实现能够灵活处理这种情况。
例如,在dx类中,cj()方法通过集合比较是否包含所有答案,当前实现未考虑到答案顺序问题。可能需要考虑题目答案的顺序对评分的影响。 - 得分计算:
sj类中对于总分是否等于100的检查是一个重要的业务规则,但在输入格式错误或数据不完整时,这个校验可能被忽略。确保数据完整性和准确性非常关键,若有缺失或者错误数据应进行及时提醒。
在sc()方法中,输出是以~符号连接每个结果,可能在格式化上需要进一步优化以符合输出需求。
第五次作业
- 并查集实现问题:
在处理引脚之间的连接时,我使用了并查集(Union-Find)来管理引脚间的连通性。在合并引脚时,发现一些连接没有被正确地识别出来,导致计算电压差时引发异常或不准确的结果。
通过仔细检查并查集的find和union操作,确保每次都对输入的引脚进行有效的查找和合并。并且在find方法中增加了路径压缩以提高效率,确保每个引脚都能够正确地找到其连接的“根节点”。 - 电压计算问题:
在计算电压时,由于设备间的复杂连接关系,电压差计算中出现了不一致的情况,尤其是在设备之间的传输电压没有正确传播。
通过引入一个队列来进行广度优先搜索(BFS),确保从VCC和GND出发的电压能够正确地传播到每个组,并且每个设备的电压计算都遵循正确的逻辑。确保每个组的电压都被更新,直到没有新组需要更新。 - 设备控制问题:
在处理控制命令时,发现设备的状态控制没有及时更新,特别是在多速控制器(MultiSpeedController)和连续调速器(ContinuousController)上,电压变化时这些设备的状态没有及时反映。
确保每个设备的状态(如档位、亮度、速度等)在控制命令执行后能够及时更新,并且在输出时能够根据设备的当前状态生成正确的结果。特别是在解析命令时,确保能够正确识别每个设备的操作类型。
第六次作业
- 设备控制逻辑的复杂性:
在设计设备控制逻辑时,尤其是在解析控制命令时,处理不同类型设备的命令方式变得复杂。每个设备都有不同的状态和行为,这使得控制逻辑的设计变得冗长。
对于不同类型的设备,我们可以通过继承和多态来统一管理设备的控制方式。例如,开关设备(SwitchDevice)、调速器(MultiSpeedController)和其他受控设备(如白炽灯、吊扇等)都可以从统一的 ControlDevice 类继承。 - 设备状态的展示:
对于每个设备的状态(如亮度、转速等),根据电压差来计算相应的状态。然而,在初期版本中,某些受控设备的状态可能计算错误,导致输出结果不准确。
在设备的状态计算中,特别是对于亮度、转速等受电压差影响的设备,确保每个设备的状态计算是基于其引脚电压差的。 - 电压计算与BFS
电压计算的部分需要通过广度优先搜索(BFS)来遍历所有组,并逐步计算每组的电压值。最初的实现中,电压的传播过程并不完全正确,可能出现电压计算不一致或遗漏的情况。
在 BFS 中,每当找到一个电压源(例如 VCC 或 GND),就从该源开始传递电压。如果当前组的电压已经计算出来,则跳过,否则继续计算。
四、改进建议
第四次作业
- 增强异常处理:学生提交了不存在的试卷,或答题卡格式错误时应详细报错。
- 评分逻辑优化:多选题中可引入更细化的部分评分规则(如权重分配)。
- 提高代码复用性:可增加 AnswerChecker 类来专门处理答案判定和计分逻辑。
- 代码复用性:sc 方法中字符串拼接逻辑重复,可提取为通用工具方法。
第五次作业
- 复杂度与函数长度:Main 类中的核心函数(如 main())复杂度较高,长度超过 200 行。
- 重复代码:部分代码逻辑(如设备状态更新)在多个设备中实现,但存在部分重复。
- 类的单一职责:Main 类职责较多,既处理输入,又控制流程,还管理设备实例,可以将输入处理与设备管理逻辑分离。
- 异常处理:当前代码缺乏对异常的处理(如输入格式错误、电压为空时的特殊情况),应添加健壮性逻辑。
第六次作业
- 代码结构:主函数 Main 中逻辑较为复杂,可将不同功能(如连接解析、设备控制命令处理、电压计算等)拆分到独立方法中。
- 注释改进:增加复杂逻辑段的注释,例如 BFS 计算电压的细节、设备状态更新的条件判断。
- 降低复杂性:引入设计模式(如策略模式)优化设备的状态更新和电压计算逻辑。
五、总结
这些题目涉及了面向对象编程(OOP)的多个方面,如类的设计、继承、多态、抽象类、接口、集合框架的使用、正则表达式的处理、以及输入输出流的操作等。经过三次题目的练习,积累了以下几点学习经验和对未来改进的建议:
-
继承与多态
通过继承和方法重写,实现了代码的复用和扩展。
-
正则表达式
掌握了如何使用正则表达式来解析和验证字符串。
-
异常处理
虽然在这些题目中没有明确提到,但在实际编程中,异常处理是保证程序健壮性的重要手段。
-
并发编程
在处理多线程或并发问题时,需要更深入地学习Java的并发机制。
-
设计模式
为了提高代码的可维护性和可扩展性,需要学习设计模式。
对教师、课程和作业组织的建议
- 教师建议:题目设计过程循序渐进,对学生理解复杂系统设计和多信息处理有较好帮助,但因复杂度较高,建议在课上增加难点讲解,尤其是信息引用管理和异常处理方面的案例分析。