一,前言
在本系列的第四至第六题目集中,我们深入探讨了智能家居和强电电路的模拟系统,并完善了答题判断程序。第4题主要聚焦于答题程序的设计与实现,要求我们实现一个多种信息输入、处理和输出的模拟考试系统,涵盖了题目信息、学生答题信息、试卷管理等模块的设计与判断逻辑,涉及较为复杂的数据结构和字符串操作。第5题通过模拟智能家居电路中的控制设备与受控设备,考察了开关、调速器、灯具和风扇等设备的状态变更和电压计算,考察了如何处理设备间的连接关系与电路的状态转化。第6题进一步在此基础上,增加了并联电路的处理要求,进一步提升了电路模拟的复杂度与层次。整体而言,这三道题目在难度上逐步递增,涵盖了基本的控制流、数据结构设计、电路模拟以及电压与电流计算等核心知识点。
二,设计与分析
答题判题程序-4
这道题要求模拟一个小型的测试,涵盖了题目信息、试卷信息、答题信息、学生信息和删除题目信息的处理。系统主要通过字符串的解析和数据结构(如列表和映射)来组织这些信息,核心功能是根据学生的答题信息与标准答案进行比对,输出判题结果。
数据结构:
题目信息:可以通过哈希表(Map<Integer, String>)来存储题号和标准答案。
试卷信息:可以采用列表来表示每张试卷及其包含的题目与分值。
学生信息:采用一个数组或集合来存储学生的学号与姓名。
答题信息:通过一个二维数组来表示每个学生的答案,序号对应试卷中的题目顺序。
删除题目信息:使用哈希集合来标记已删除的题目。
代码设计:
输入与输出的格式处理:重点是如何将输入的不同信息类型(题目、试卷、学生、答案等)解析成合适的格式并存储。使用正则表达式或分割字符串的方法来提取各个字段。
判题逻辑的实现:对于学生的答卷,要根据题目的标准答案进行比对,计算得分。特别需要处理题目缺失的情况,确保删除题目后,系统能正常处理,并显示对应的“无效”提示。
优化建议:
-
简化重复代码
result 变量的赋值部分重复了很多字符串拼接的代码。我们可以将它提取到一个单独的函数中,从而提高代码可读性和复用性。 -
改进条件判断
目前的判断条件较多,并且有些条件判断(如else if (scores == 0)) 不够简洁。可以通过构建一个条件映射来简化这些判断逻辑。 -
增强代码可读性
通过合适的命名和简化的判断条件使得代码更加清晰。
例如:
优化前
优化后:
类图:
Input_Prossing 类负责处理用户输入,解析不同格式的题目、试卷、学生答卷等信息。
Question 类是所有题目的基类,包含基本的题目信息。
MultipleChoiceQuestion 类是继承自 Question 类的子类,用于表示选择题。
FillInTheBlankQuestion 类是继承自 Question 类的子类,用于表示填空题。
Paper 类表示一份试卷,包含试卷的题目和分数信息。
AnswerSheet 类表示一张答卷,存储了学生的答案信息。
Student 类表示一个学生的信息。
Question 类作为所有题目的父类,后续的 MultipleChoiceQuestion 和 FillInTheBlankQuestion 类分别实现了选择题和填空题,体现了面向对象编程中的继承与多态。Paper 和 AnswerSheet 类分别管理试卷和答卷的数据。Input_Prossing 类负责从用户输入中解析信息并进行处理,是程序的核心。
采坑心得
1.使用哈希表存储学生试卷时,只用试卷号作为键值,多个学生时,试卷号相同会覆盖掉前一个学生的答卷,导致输出错误,不符合题目要求
这样会出错
改进
2.输入格式与边界处理不足
输入的处理存在潜在的边界问题,比如学生的答案格式不统一、无效输入的情况没有得到及时处理。此时程序可能崩溃或得出错误的结果。
增强输入校验:对于外部输入(如用户提交的答案、试卷内容等),一定要进行详细的验证和校验,确保输入符合预期格式。对于异常或无效输入,可以提前抛出异常并及时给出错误提示。
改进建议:
1.改进类命名和代码结构
问题:
类命名方面有些地方不太规范,例如 Input_Prossing 应该改为 InputProcessing,符合 Java 的命名规范(首字母大写,后续单词首字母大写)。
类与方法的注释缺失,特别是关键方法如 process()、input_processing() 等应有清晰注释,说明功能和参数。
改进建议:
改正命名规范:将 Input_Prossing 改为 InputProcessing,delateQuestionNumSet 改为 deletedQuestionNums,使其遵循 Java 命名规范。
方法注释:为每个方法添加适当的注释,特别是复杂的方法,如 process() 和 input_processing(),方便后期维护和协作开发。
2.优化 process() 方法的逻辑
问题:
process() 方法非常庞大,职责过多,涉及学生信息验证、答卷评估、打印结果等。
if 和 else 条件过多,导致代码阅读性差,逻辑不清晰。
改进建议:
拆分方法:将 process() 方法拆分为多个小方法,每个方法处理一个单一的逻辑任务,比如验证学生、评估答案、生成结果等。
public void process() {
for (String studentId : studentMap.keySet()) {
processStudent(studentId);
}
}
private void processStudent(String studentId) {
// 获取答卷、试卷等
// 调用子方法进行评分和输出
}
private void evaluateAnswers(AnswerSheet answerSheet, Paper paper) {
// 根据答案计算分数
}
3.改进题目删除逻辑
问题:
当前删除题目的逻辑较为简单,delateQuestionNumSet 只是一个存储删除题号的集合,并没有有效标记题目是否有效。
删除题目时,代码中注释掉了 questionMap.remove(delateQuestionNum),表示题目没有真正被移除,可能导致评分时存在问题。
改进建议:
逻辑改进:当题目被删除时,除了标记题目无效外,还应从题库中移除对应的题目,并在评分时忽略已删除的题目。
public void deleteQuestion(int questionNum) {
questionMap.remove(questionNum); // 从题库中移除题目
deletedQuestionNums.add(questionNum); // 标记删除题目
}
4.避免使用 LinkedHashMap 和 HashSet 的混合使用
问题:
LinkedHashMap 和 HashSet 用于存储题库、试卷库等信息,但它们的使用场景和特点未必完全符合业务需求。
改进建议:
优化数据结构:在存储题库时,如果不需要按插入顺序遍历,可以使用 HashMap 代替 LinkedHashMap。对于已删除的题目,HashSet 可以替换为 HashMap 或其他更合适的数据结构,确保高效存取。
LinkedHashMap<Integer, Question> questionMap = new HashMap<>(); // 如果不需要按顺序
家居强电电路模拟程序-1
本题设计了一个智能家居强电电路模拟系统,其中涉及开关、调速器、灯具和风扇的控制与模拟。每个设备都有输入和输出端口,通过电压的变化来控制设备的工作状态。
控制设备的状态变化:开关、调速器和连续调速器的状态变化都直接影响到设备的工作状态。例如,调速器通过不同的档位来控制输出电压,开关则是简单的开关状态切换。
受控设备的模拟:灯具的亮度和风扇的转速都是通过电压差来控制的,需要考虑不同设备的具体工作原理和电压区间。
控制设备:
开关(K):用于控制电路开关,状态为0或1(打开或关闭)。
分档调速器(F):用于调节电压的档位,支持3档、4档调节,输出电压为输入电压的不同倍数(例如0、0.3、0.6、0.9倍)。
连续调速器(L):可以根据位置比例调节输出电压,数值范围为[0.00-1.00],精确到小数点后两位。
受控设备:
白炽灯(B):根据电压差调节亮度,电位差决定亮度的值,电位差为10V时,亮度为50 lux。
日光灯(D):有两种状态,电位差为0时灭灯,非零时亮灯,亮度为180 lux。
吊扇(R):根据输入电压差调节转速,电位差为80V时,转速为80转/分钟,150V时为360转/分钟,超过150V时转速维持在360转/分钟。
设计:
控制设备模拟:
开关类(K):应有输入引脚和输出引脚,状态控制方法用于切换开关的开关状态。
调速器类(F、L):应有输入引脚和输出引脚,调节方法可以根据指令增加或减少档位,连续调速器类还需要设置档位比例。
受控设备模拟:
白炽灯类(B):应根据电位差计算亮度。
日光灯类(D):只有两种状态,根据电位差决定亮灭状态。
吊扇类(R):根据输入电位差计算转速。
电路结构:
对于每条电路连接信息,系统需要记录每个引脚的连接关系,并按要求计算每个设备的输出状态。
需要处理多设备串联或并联的情况,考虑如何递归或迭代地计算各设备的输出值。
输入输出管理:
输入指令应解析为具体的设备控制操作,并更新电路状态。
输出时需要按照格式返回每个设备的状态或参数。
代码设计:
电压计算与状态转换:对于灯具和风扇的工作状态,需要根据输入的电压差进行映射,使用适当的算法来计算每个设备的状态。
连接与配置管理:设计良好的连接管理系统非常重要。每个设备通过引脚连接,且设备间的电压差会影响设备的工作状态。因此,设备之间的连接关系需要清晰管理。
类图:
Device 类(抽象类)这是所有设备的基类。每个具体设备类都继承自 Device。
Switch 类继承自 Device,表示一个开关设备。
功能:
可以切换状态(打开或关闭)。toggle() 方法用于改变开关状态。
通过 updateState() 更新开关的引脚状态:如果开关打开,pin2 输出 pin1;如果关闭,pin2 输出 GND。
getState() 返回当前的开关状态(如 "turned on" 或 "closed")。
StepSpeedControl 类
StepSpeedControl 类继承自 Device,表示一个分档调速器设备。
功能:
支持调整档位(0 到 3,分别对应 0V、66V、132V 和 198V)。
通过 increaseStep() 和 decreaseStep() 增加或减少档位。
updateState() 根据当前档位调整输出电压并更新设备状态。
getState() 返回当前档位(如 "2" 表示第2档)。
ContinuousSpeedControl 类
ContinuousSpeedControl 类继承自 Device,表示一个连续调速器设备。
功能:
支持设置一个在 0 到 1 之间的连续电压比例(即调速)。
setSpeed() 方法设置电压比例。
updateState() 根据设置的速度调整输出电压并更新设备状态。
getState() 返回当前速度(如 "0.75" 表示 75% 的最大速度)。
IncandescentLight 类
IncandescentLight 类继承自 Device,表示一个白炽灯设备。
功能:
根据输入电压(由调速器设备提供的电压)调整亮度。
updateState() 根据电压调整亮度,并更新设备状态。
getState() 返回当前亮度,单位为 lux(如 "200" 表示最大亮度)。
FluorescentLight 类
FluorescentLight 类继承自 Device,表示一个日光灯设备。
功能:
根据输入电压调整亮度。电压为 0 时亮度为 0,否则亮度固定为 180 lux。
updateState() 更新亮度状态。
getState() 返回亮度(如 "180" 表示亮度)。
CeilingFan 类
CeilingFan 类继承自 Device,表示一个吊扇设备。
功能:
根据输入电压控制风速。电压较低时风速为 0(停转),电压较高时风速为 360 rpm。
updateState() 根据输入电压计算当前风速,并更新设备状态。
getState() 返回风速(如 "180" 表示风速为 180 rpm)。
Circuit 类
Circuit 类用于管理电路中的所有设备,并负责设备间的连接和状态更新。
功能:
设备按类型顺序排序(例如,开关优先,接着是调速器,最后是灯光和风扇)。
update() 方法模拟设备状态更新。通过设备之间的电压传递,更新每个设备的状态。
displayStates() 输出设备当前的状态,显示每个设备的状态信息(如风速、亮度等)。
采坑心得:
- 未考虑全面,对于多个开关的情况会出错
2. 对于开关可能连在调速器后未考虑,如果电路出现调速器在开关前,会输出错误
修改后
3.在 Circuit 类中的 update 方法中,设备是按顺序更新的,但 setPin1 和 setPin2 的赋值逻辑是直接基于前后设备的状态,这可能会导致电压值的异常传递。例如,最后一个设备的 Pin2 被设为 GND,但是如果设备未正确初始化,电压传递就会出现问题。
修改:
改进建议
-
代码结构优化
类的职责分离:
Device 类的设计过于庞大,负责多种不同设备的功能,这违背了单一职责原则。可以考虑进一步抽象设备的共同接口或基类,拆分成多个接口或父类。例如,可以为每类设备的不同控制方式(如开关控制、调速控制)创建接口,设备类实现相关接口。
接口设计:Switch、StepSpeedControl、ContinuousSpeedControl 等类可以通过一个 DeviceController 接口进行更好的扩展和实现。这样不仅减少代码重复,还可以提高扩展性。
2.简化 processConnections 方法
简化 processConnections 方法的实现:
目前的实现是通过分割字符串来解析连接,虽然能达到目的,但不够灵活。你可以考虑将连接信息保存为一个更具结构化的对象,例如:
class Connection {
private String device1;
private String device2;// 构造函数,getter,setter等方法
}
这样可以减少错误和提高可读性。 -
控制逻辑的简化和增强
processControl 方法中的控制代码可以重构:
控制逻辑有很多重复的字符串解析和设备查找操作,可以考虑封装成更具可读性的结构。
private static void processControl(ListcontrolInput, List devices) {
for (String control : controlInput) {
Device device = findDeviceById(devices, control.substring(0, 2));
if (device == null) continue;
// 根据设备类型进行不同的控制
if (device instanceof Switch) {
((Switch) device).toggle();
} else if (device instanceof StepSpeedControl) {
handleStepSpeedControl((StepSpeedControl) device, control);
} else if (device instanceof ContinuousSpeedControl) {
handleContinuousSpeedControl((ContinuousSpeedControl) device, control);
}
}
}
这里通过重构,将 processControl 简化成不同的具体操作,增加了可维护性和可扩展性。 -
电路类的优化
Circuit 类中的设备更新部分可以进一步优化:
目前的 update() 方法有点复杂,涉及设备顺序管理、设备状态更新等逻辑。可以考虑将设备的连接和状态更新分开,提取成独立的类来管理,尤其是关于电路状态的管理。电路的连接和更新逻辑可以拆分成 ConnectionManager 和 StateUpdater 等类。
设备连接部分应单独提取出来,而不是由 Circuit 类直接处理。
Circuit.update() 中设备的排序逻辑可能不适合放在这里。你可以让设备自己处理自己内部的排序(如在每个设备类中进行),使 Circuit 更加简洁。
** 家居强电电路模拟程序-2**
这一题在第5题的基础上,增加了并联电路的模拟。每条并联电路由多条串联电路组成,模拟的关键在于如何正确处理并联电路中多个串联电路的电压和电流关系。
并联电路的电流分配:在并联电路中,每条串联电路的电流是独立的,但它们共享相同的电压。模拟时要确保每个串联电路的电压计算和电流分配都正确。
设备之间的相互作用:并联电路中的每个设备都需要考虑输入电压差的变化,确保电路的稳定性和正确性。
代码设计心得:
电压和电流的计算:通过适当的电流和电压计算公式来模拟电路中的能量传递。对于并联电路,需要确保电压不受串联电路数量的影响。
连接管理:设计清晰的连接管理系统,通过图形化工具来帮助理解和管理复杂的电路连接关系。
类图:
抽象类:Device
功能:
作为所有设备的基类,定义了通用属性(如引脚、电阻、设备ID)和方法(如状态更新和状态获取)。
主要方法:
updateState:更新设备的状态,由子类实现。
getState:获取设备当前状态,由子类实现。
getResistance:返回设备的电阻值。
设备子类
- Switch
模拟开关设备,支持切换状态(开/关)。
电阻:固定为0。
状态:
开关状态为“closed”表示打开。
开关状态为“turned on”表示关闭。 - StepSpeedControl
模拟分档调速器,具有4个档位(0-3)。
电阻:固定为0。
状态:
返回当前档位(0、1、2、3)。
功能:
可调整档位。
根据档位计算输出电压。 - ContinuousSpeedControl
模拟连续调速器,速度为0.00到1.00的比例。
电阻:固定为0。
状态:
返回当前速度值(保留两位小数)。
功能:
根据速度比例计算输出电压。 - IncandescentLight
模拟白炽灯,亮度随电压变化。
电阻:固定为10。
状态:
返回亮度值(lux)。
亮度逻辑:
电压低于10V时亮度为0,10V时为50 lux,220V时为200 lux。 - FluorescentLight
模拟日光灯。
电阻:固定为5。
状态:
返回亮度值(lux),通常为0(未点亮)或180(点亮)。 - CeilingFan
模拟吊扇设备。
电阻:固定为20。
状态:
返回风速(0到360)。
风速逻辑:
电压小于80V时风速为0,电压大于或等于150V时风速为360。 - GroundFan
模拟落地扇设备。
电阻:固定为20。
状态:
返回风速(RPM)。
风速逻辑:
电压范围决定风速,支持4个区间(80-100、100-120、120-140、140及以上)。
电路类 - 抽象类:Circuit
定义了通用电路属性和方法,包括输入电压、输出电压、设备列表等。 - ConcatenatedCircuit(串联电路类)
模拟串联电路的行为。
功能:
存储串联电路的设备列表。
计算总电阻。
判断电路是否短路或断路。
更新设备状态。
方法:
calculateResistance:计算总电阻。
isBreak:判断电路是否断路。
isShort:判断电路是否短路。 - ParallelCircuit(并联电路类)
模拟并联电路的行为。
功能:
存储并联电路的串联电路列表。
计算总阻抗。
判断并联电路是否短路或断路。
更新设备状态。
方法:
getresistance:计算并联电路的总阻抗。
isBreak:判断并联电路是否断路。
Break:判断并联电路是否有短路。
踩坑心得
- 对于电阻比例分压计算各电器的电压初始值一直为VCC,导致输出错误
修改后
- 未考虑并联电路的短路和断路情况,输出错误
增加判断条件处理此类情况
改进建议
1.使用面向对象的多态设计
问题: Device 类及其子类中有许多重复的属性和方法(如 setPin1、setPin2)。
改进:
提取公共逻辑到基类或接口中。
使用接口定义行为,比如定义 Updatable 接口:
interface Updatable {
void updateState();
String getState();
}
设备类实现接口后,可以统一处理设备行为,减少类型检查的分支逻辑(如 instanceof)。
2.优化电路连接与状态更新
问题:
串联电路和并联电路的逻辑复杂,且与设备的行为耦合紧密。
状态更新逻辑重复且难以维护。
改进:
在 Circuit 类中提取公共逻辑,减少 ConcatenatedCircuit 和 ParallelCircuit 中的重复代码。
引入事件驱动的更新机制,使用观察者模式通知电路设备更新状态。
3.优化电路连接与状态更新
问题:
串联电路和并联电路的逻辑复杂,且与设备的行为耦合紧密。
状态更新逻辑重复且难以维护。
改进:
在 Circuit 类中提取公共逻辑,减少 ConcatenatedCircuit 和 ParallelCircuit 中的重复代码。
引入事件驱动的更新机制,使用观察者模式通知电路设备更新状态。
三,总结
在本阶段的三次题目集中,我不仅深入理解了面向对象编程的核心概念,如类与对象、继承与多态、数据结构的选择与应用等,还通过具体的项目实现,加深了对设计模式和算法优化的理解。例如,在智能家居电路模拟中,我学习了如何合理设计类之间的关系,掌握了模拟复杂电路系统的技巧,但在第二次迭代时,第一次设计的结构显得不太合理,由于加入了串联和并联类,在连接电路时由于类型不同导致难度加大;在答题程序设计题中,我加深了对集合、哈希表等数据结构的使用理解,并学会了如何高效地处理大规模数据。在这些实践中,我也发现了自己对一些高级算法和优化技巧的掌握还不够。
希望每道题的示例涉及多个方面,有时候示例和自己修改的示例以及同学的示例都能通过,但测试一直不通过,也找不到原因。