答题判题程序终章与家居强电电路模拟程序
题目要求
1.设计实现答题程序,模拟一个小型的测试,要求输入题目信息、试卷信息、答题信息、学生信息、删除题目信息,根据输入题目信息中的标准答案判断答题的结果
家居强电电路模拟程序
2.智能家居是在当下家庭中越来越流行的一种配置方案,它通过物联网技术将家中的各种设备(如音视频设备、照明系统、窗帘控制、空调控制、安防系统、数字影院系统、影音服务器、影柜系统、网络家电等)连接到一起,提供家电控制、照明控制、电话远程控制、室内外遥控、防盗报警、环境监测、暖通控制、红外转发以及可编程定时控制等多种功能和手段。与普通家居相比,智能家居不仅具有传统的居住功能,兼备建筑、网络通信、信息家电、设备自动化,提供全方位的信息交互功能。
一、前言
对于这几周开始的Java答题判题程序的编写对我压力有点大,因为我对编程感觉总是慢半拍。虽然痛苦但也学习到了一些知识,过程艰辛但也有些许收获,让我对Java编程了解到了更多。
对于答题判题程序而言,这是这次程序最终的迭代,难度又提升了
本次迭代在以前的基础上新增加了以下四个功能
1、输入选择题题目信息
2、输入填空题题目信息
3、输出顺序变化
4、多张试卷信息
本题考虑多个同学有多张不同试卷的答卷的情况。输出顺序优先级为学号、试卷号,按从小到大的顺序先按学号排序,再按试卷号。
本次程序我利用了正则表达式来对输入合理性进行判断,然后利用继承题目类BaseQuestion类来对多选题以及填空题进行处理,可以轻松地扩展现有的题目类,而不会影响已有的代码。SingleQuestion类、MultipleChoiceQuestion类和FillInQuestion类可以覆盖或重写BaseQuestion父类的方法,以提供特定于子类的行为,这增强了代码的灵活性和可扩展性。层次结构便于利用链表来存储警告信息,在判断完成后再输出,输出采用 TreeMap对学号、试卷号,按从小到大的顺序先按学号排序,再按试卷号排序,对输出结果有了更好的输出。
对于新题目家居强电电路模拟程序
控制设备模拟:包括开关、分档调速器、连续调速器
受控设备模拟:灯、风扇。两种设备都有两根引脚,通过两根引脚电压的电压差驱动设备工作。
通过控制设备来对受控设备进行控制,控制电路分为串联电路以及并联电路以及混合电路。
第一次家居强电电路模拟程序相对来说比较简单,但为了下一次迭代就要好好设计类,尽量让下一次代码能够复用。本次程序我采用Device作为设备主类,控制设备以及受控设备都继承来自Device,第二次创建了串联与并联的类,继承Device,知识点主要涉及到利用继承来对代码复用,以及利用HashMap来存储相关的数据,然后利用正则表达式来对输入的信息进行分割,最终将结果输出,第二次家居强电电路模拟程序相对于第一次程序难度大幅提升,串并联电路结合出现问题导致输出总是达不到想要的结果,难度对我来说大幅提升。
在面对新的需求的时候,我尝试去修改类以及部分逻辑,但最终因为前面基础设计的类以及逻辑不够严谨,导致修改总是出问题,最终在无可奈何的情况下进行从头开始设计,但最终因为题目理解上出错,导致还有部分测试点还未能通过,后续因为时间不足导致没能继续进行修改。总体来说这次如果前面设计类比较合理,逻辑比较正确,修改增加新的功能不会太难,但也要画大量时间去调试程序以达成通过测试点的目的。
二、设计与分析
本次答题判题程序最终的迭代
通过继承 BaseQuestion 类,SingleQuestion、MultipleChoiceQuestion 和 FillInQuestion 类可以复用基类中定义的属性和方法,同时也可以根据各自的特性添加特定的功能。这种方式不仅减少了代码冗余,还提高了代码的可维护性和扩展性。
class SingleQuestion extends BaseQuestion {
public SingleQuestion(int id, String content, String standardAnswer) {
super(id, content, standardAnswer);
}
class MultipleChoiceQuestion extends BaseQuestion {
public MultipleChoiceQuestion(int id, String content, String standardAnswer) {
super(id, content, standardAnswer);
}
class FillInQuestion extends BaseQuestion {
public FillInQuestion(int id, String content, String standardAnswer) {
super(id, content, standardAnswer);
}
构造方法接受三个参数,分别是题目ID、题目内容和标准答案。
super(id, content, standardAnswer):调用父类 BaseQuestion 的构造方法,初始化题目ID、内容和标准答案。
这三个hashMap来保存问题、试卷、以及答题试卷,与前面设计的大差不差
Map<Integer, Paper> papers = new HashMap<>();
Map<String, Student> students = new HashMap<>();
Map<Integer, BaseQuestion> questionBank = new HashMap<>();
后面由于要将学生作答以及按学号排序和按试卷编号排序输出结果,所以创建TreeMap来对输出进行排序
TreeMap<String, TreeMap<Integer, AnswerSheet>>answerSheets = new TreeMap<>(); // 按学号排序
TreeMap<Integer, String> alerts = new TreeMap<>(); // 按试卷编号排序
下面是具体类图

以下是时序图

对于家居强电电路模拟程序第一次大作业
总体来说难度没那么大毕竟是第一次作业
1.设备类(抽象类)
考虑到该题是将许多种类的设备串联起来,所以可以设置一个父类 设备类,将其余的设备都继承这个类,使其具有可拓展性。此外需要在设计一个getStatus的抽象方法用于获取设备状态。
2.继承设备类的其他电器类
根据题目依次设计开关,分档调速器,连续调速器,灯类,吊扇类。
设备类定义
Switch:开关设备,可以打开或关闭。
SteppedSpeedController:步进速度控制器,有四个等级的速度设置。
ContinuousSpeedController:连续速度控制器,可以在0.00到1.00之间任意设置速度。
IncandescentLight:白炽灯,亮度根据电压差变化。
FluorescentLight:荧光灯,亮度为固定值180或0。
CeilingFan:吊扇,转速根据电压差变化。
主类 Main
Map<String, Device> devices:存储所有设备对象。
Map<String, String> connections:存储设备之间的连接关系。
List
输入处理
读取用户输入的每一行,解析出设备连接关系和控制动作。
连接关系格式:[K1 K2] 表示设备K1的输出连接到设备K2的输入。
控制动作格式:#K1 表示切换开关K1的状态,#F1+ 表示增加步进速度控制器F1的速度等级,#L1:0.5 表示设置连续速度控制器L1的速度为0.5。
设备初始化
根据连接关系和控制动作初始化所有设备对象。
处理控制动作
遍历所有控制动作,根据动作类型调用相应的方法更新设备状态。
计算电压差并更新设备状态
根据设备之间的连接关系计算电压差,并更新受控设备的状态。
输出设备状态
按照特定顺序输出所有设备的状态。设备类型按字母顺序排序,同类型设备按ID数字排序。
输入处理
点击查看代码
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (line.equals("end")) {
break;
}
if (line.startsWith("[")) {
String[] parts = line.substring(1, line.length() - 1).split(" ");
connections.put(parts[0], parts[1]);
} else if (line.startsWith("#")) {
controlActions.add(line);
}
}
设备初始化
点击查看代码
for (Map.Entry<String, String> entry : connections.entrySet()) {
String deviceId = entry.getKey().split("-")[0];
if (!devices.containsKey(deviceId)) {
// 根据设备ID前缀创建相应类型的设备对象
if (deviceId.startsWith("K")) {
devices.put(deviceId, new Switch(deviceId));
} else if (deviceId.startsWith("F")) {
devices.put(deviceId, new SteppedSpeedController(deviceId));
} else if (deviceId.startsWith("L")) {
devices.put(deviceId, new ContinuousSpeedController(deviceId));
} else if (deviceId.startsWith("B")) {
devices.put(deviceId, new IncandescentLight(deviceId));
} else if (deviceId.startsWith("R")) {
devices.put(deviceId, new FluorescentLight(deviceId));
} else if (deviceId.startsWith("D")) {
devices.put(deviceId, new CeilingFan(deviceId));
}
}
}
处理控制动作
点击查看代码
for (String action : controlActions) {
if (action.startsWith("#K")) {
String switchId = action.substring(1);
((Switch) devices.get(switchId)).toggle();
} else if (action.startsWith("#F")) {
Pattern pattern = Pattern.compile("#(F\\d+)([+-])");
Matcher matcher = pattern.matcher(action);
if (matcher.find()) {
String controllerId = matcher.group(1);
String operation = matcher.group(2);
Device device = devices.get(controllerId);
if (device instanceof SteppedSpeedController) {
if (operation.equals("+")) {
((SteppedSpeedController) device).increase();
} else if (operation.equals("-")) {
((SteppedSpeedController) device).decrease();
} else {
System.out.println("Error: Invalid operation " + operation);
}
} else {
System.out.println("Error: Device " + controllerId + " is not a SteppedSpeedController.");
}
} else {
System.out.println("Error: Invalid action format " + action);
}
} else if (action.startsWith("#L")) {
String[] parts = action.split(":");
String controllerId = parts[0].substring(1);
double level = Double.parseDouble(parts[1]);
((ContinuousSpeedController) devices.get(controllerId)).setLevel(level);
}
}
计算电压差并更新设备状态
点击查看代码
for (Map.Entry<String, String> entry : connections.entrySet()) {
String fromPin = entry.getKey();
String toPin = entry.getValue();
Device fromDevice = devices.get(fromPin.split("-")[0]);
Device toDevice = devices.get(toPin.split("-")[0]);
double voltage = 0;
if (fromPin.equals("VCC")) {
voltage = 220;
} else if (fromDevice instanceof Switch) {
voltage = ((Switch) fromDevice).getState() == 1 ? 220 : 0;
} else if (fromDevice instanceof SteppedSpeedController) {
voltage = ((SteppedSpeedController) fromDevice).getLevel() * 220;
} else if (fromDevice instanceof ContinuousSpeedController) {
voltage = ((ContinuousSpeedController) fromDevice).getLevel() * 220;
}
if (toDevice instanceof IncandescentLight) {
((IncandescentLight) toDevice).setBrightness(voltage);
} else if (toDevice instanceof FluorescentLight) {
((FluorescentLight) toDevice).setBrightness(voltage);
} else if (toDevice instanceof CeilingFan) {
((CeilingFan) toDevice).setSpeed(voltage);
}
}
模块化设计:每个设备类都有明确的职责和方法,使得代码易于理解和维护。
输入解析:通过正则表达式和字符串分割,有效地解析用户输入,确保了程序的健壮性。
状态更新:通过设备之间的连接关系和控制动作,动态地更新设备状态,实现了家庭自动化系统的核心功能。
输出格式:按照特定顺序输出设备状态,便于用户查看和调试。
下面是类图

下面是顺序图

第二次家居强电电路模拟程序大作业
相对于第一次来说难度大大增强,导致逻辑需要理清楚,串联并联之间的衔接需要控制好,就像写物理题目一样,需要一步步向下思考。
1、电路设备类:描述所有电路设备的公共特征。
2、受控设备类、控制设备类:对应受控、控制设备
3、串联电路类:一条由多个电路设备构成的串联电路,也看成是一个独立的电路设备
4、并联电路类:继承电路设备类,也看成是一个独立的电路设备
我还定义了一个输入类与输出类,使得代码输入输出不显得main函数更加复杂。

时序图与上次基本大差不差就是加入串联并联为总电路

三、采坑心得
1.仔细审题,第一次家居强电电路模拟程序由于没有注意输出顺序导致一直出错,后来仔细审题完成后发现问题。后来设计按顺序输出,还有就是出现小数要进行去尾法,开始没有导致时不时就有答案错误,还找不到问题所在。

2.写代码需要多写注释
由于我对Java编程能力没有那么强,每次完成大作业的最后一题都要花费长时间,通常不能够一次完成作业,都需要花费好几天,这个过程写注释就非常需要了,通常调侃说:不写注释的代码只有自己本人和上帝看得懂。但只要三天后就只有上帝看到的懂了。这句话虽然有点玄学,但确实是编程人的实际情况,每次我写写代码把战线拉长的时候,一旦忘了写注释就一朝回到解放前,又要重新开始捋捋思路。也会浪费长时间,导致代码不连贯,最好还是能够一口气将代码完成。写注释的时候最好的是能将每一个变量的作用,每一个方法的作用,每一个代码块的作用都要给出注释,因为当代码量大起来后,你可以通过注释去了解过去的自己写这些代码的目的,而不用去逐条逐条的看代码,逐条逐条看代码是非常恐怖了。所以写代码加注释是非常重要的。
3.画一下顺序图与类图的必要性
画顺序图能帮助我们更好理清思路,你写代码的时候可以先构造一个大概的框架,便于你进行逻辑理解,然后在这个框架的基础上进行画顺序图,通过画顺序图的过程能够发现很多自己欠缺 考虑的方面,能让自己对框架逐渐完善,对于程序流转步骤会有更加清晰,有利于编写代码。然后再通过顺序图完成相应的类图,最后进行编写代码。虽然画顺序图和类图会比较花费时间,但如果时间充足的话,能对自己写代码有很好的帮助。
四、改进建议
1.拓展性方面:在编写这三次题目集时,我思考了许多,其中存在的最严重的问题还是代码的拓展性问题。开始我很少考虑代码的拓展性,导致开始做题之后总是用大把的时间用在代码的重构上,造成效率的降低,又要花费大量时间去编写代码。
2.类的设计方面:虽然经过前三次大作业的训练后,我能大致设计出题目中类的结构和关系,但当我在写类中的方法时,经常会不自觉的加入这个类中不应处理的内容,这违背了单一职责原则。
3.由于每次的大作业都需要加以修改,因此有部分的代码可能原先有用,而修改后该方法可能不再被使用或者方法被修改后不再适用原来的用法,会导致思路突然的中断。当这种情况发生时,原先精心设计的类结构、实现的方法可能在新的需求下变得冗余或过时。这种突然的变化不仅会影响我们的编写效率,还可能导致代码质量的下降,增加维护成本。为了应对这种情况,我需要培养良好的代码管理和重构习惯。保持代码的模块化设计,确保每个模块都有清晰的职责和边界。这样,在需要修改或删除某个功能时,我们可以只关注相关模块,减少对其他部分的影响。
4.家居强电电路模拟程序是全新的题目类型,其大量涉及到继承和多态的应用,而我对于这方面的使用还不熟练,致使代码中含有大量冗杂的部分,不便于后续修改。后续应当合理地设计类结构,有效地利用这些特性来简化代码、提高代码的可读性和可维护性。减少我的代码中充满了冗杂的部分,方便阅读,更难以进行后续的修改和扩展。
5.对于我设计的类,他们之间的关系没有设计清楚,我应该根据给出迭代的最终电路重新设计,考虑好电路之间的连接关系,以及更新设备的状态,运用OOP的设计模式来实现我的代码。对于电路系统-2要改进Main类中几个方法处理数据的情况以及正确打印设备状态信息的代码。
6.优化数据结构的选择
在处理大量数据时,选择合适的数据结构非常重要。
使用高效的数据结构:例如,在答题判题程序中,使用 TreeMap 来存储学生的答题记录,可以高效地进行排序和查找。在家居强电电路模拟程序中,使用 HashMap 来存储设备之间的连接关系,可以快速地查找和更新设备状态。
避免冗余数据:在设计数据结构时,尽量避免冗余数据,减少内存占用。例如,在答题判题程序中,可以使用 Set 来存储唯一的学生学号,避免重复存储。
五、总结
在这次的Java答题判题程序和家居强电电路模拟程序的开发过程中,我深刻体会到了软件工程的重要性,尤其是前期设计和规划对后期开发的影响。通过这次实践,我学到了很多宝贵的经验,同时也发现了自身在编程技术和项目管理上的不足之处。
首先,在答题判题程序的开发中,我采用了面向对象的设计思想,通过创建BaseQuestion基类来封装题目共有的属性和行为,如题目ID、内容和标准答案等。基于这一基类,我又分别实现了SingleQuestion、MultipleChoiceQuestion和FillInQuestion三个子类,以适应不同类型题目的特殊需求。这种设计不仅提高了代码的可复用性和可扩展性,也为后续的功能迭代打下了坚实的基础。例如,在处理多张试卷信息时,通过引入TreeMap来存储学生的答题记录,并按照学号和试卷号排序,使得程序能够高效地管理大量数据,同时保证了输出结果的准确性。
然而,在开发家居强电电路模拟程序的过程中,我也遇到了不少挑战。最初的设计较为简单,主要是为了满足基本功能需求。随着项目的深入,特别是引入了串联电路和并联电路的概念后,原有的设计暴露出了一些问题,比如类结构过于扁平、代码冗余度高、逻辑复杂度增加等。这些问题直接导致了代码的可读性和可维护性下降,同时也增加了后期调试和优化的难度。为了解决这些问题,我不得不重新审视整个项目的架构,通过引入抽象类和接口来增强系统的灵活性和可扩展性,同时优化了数据结构的选择,如使用HashMap来存储设备间的连接关系,从而提高了程序处理复杂电路的能力。
在解决实际问题的同时,我也意识到了理论知识的重要性。例如,正确应用继承和多态不仅可以简化代码,还能提高程序的性能。此外,遵循单一职责原则和开闭原则等设计模式,有助于构建更加健壮和灵活的软件系统。但是,理论知识的掌握并不意味着可以直接转化为实际操作能力,这需要大量的实践积累。在未来的项目中,我会更加注重理论与实践相结合,不断提升自己的技术水平。
总的来说,这次的项目经历是一次宝贵的学习机会。它不仅加深了我对Java语言的理解,也让我学会了如何更有效地进行团队合作和个人时间管理。更重要的是,这次经历教会了我面对困难时不放弃,勇于探索未知领域的重要性。我相信,随着经验的不断积累和技术水平的逐步提升,未来无论遇到什么样的挑战,我都能够从容应对。

浙公网安备 33010602011771号