22207223-王颖对于答题判题程序4,家居强电电路模拟程序1~2的总结

一、前言

1.答题判题程序4

1.1知识点

(1)输入和输出处理:

  • 需要处理多种格式的输入,包括题目信息、试卷信息、学生信息、答题信息和删除题目信息。

  • 输出需要根据不同的条件生成不同的结果,包括警示信息、答题信息、判分信息等。

(2)面向对象编程:

  • 使用面向对象的设计思想,定义了多个类,如QuestionPaperStudent等,分别用于管理题目、试卷和学生信息。

  • 通过继承和多态实现不同类型题目的判题逻辑。

(3)异常处理:

  • 自定义异常类InputFormatException用于处理输入格式错误。

(4)集合和数据结构:

  • 使用MapSetList等集合类来存储和管理数据。

  • 使用HashMapHashSet来快速查找题目和学生信息。

(5)字符串处理和正则表达式:

  • 使用正则表达式解析输入字符串,提取题目编号、内容、答案等信息。

  • 字符串的比较和处理是判题逻辑的核心部分。

(6)排序和比较

  • 实现了Comparable接口,用于对答卷进行排序,确保输出顺序符合要求。

1.2题量

  题目涉及的类和方法比较多,代码量也就比较多。试卷涉及的题目类型包括单选题、多选题和填空题,每种题型都有不同的判题逻辑。需要处理的输入信息种类多样,并且要求对每种输入进行格式验证和错误处理。

1.3难度

  • 中等到偏难:题目涉及多个编程知识点的综合应用,包括面向对象编程、集合类使用、正则表达式、异常处理等。

  • 复杂性高:需要处理多种输入格式,判题逻辑复杂,尤其是多选题和填空题的部分正确判定。

  • 细节要求高:输出格式和顺序有严格要求,需要仔细处理每种可能的输入情况和错误。

2.家居强电电路模拟程序1

2.1知识点

(1)面向对象编程:

  • 题目要求使用Java编程语言实现电路模拟系统,涉及到类的继承、抽象类和接口等OOP概念。

  • 设备类的设计需要体现出继承的层次结构,例如CircuitDevice作为所有设备的基类,ControlDeviceControlledDevice作为中间层次的抽象类。

(2)电路模拟:

  • 题目模拟了多种电路设备,包括开关、分档调速器、连续调速器、白炽灯、日光灯和吊扇。

  • 需要理解每种设备的工作原理,如何通过输入电压和设备状态来计算输出电压或设备的工作状态(如灯的亮度、风扇的转速)。

(3)输入解析与处理:

  • 题目要求解析多种格式的输入,包括设备连接信息和设备控制命令。
  • 需要使用正则表达式和字符串处理技术来提取和处理输入信息。

(4)电路连接与电压传播:

  • 设备之间的连接信息用来模拟电路的连接结构,电压从电源传递到各个设备。
  • 需要模拟电压在串联电路中的传播,计算每个设备的输入和输出电压。

2.2题量

题目涉及多个类的设计和实现,每个类代表一种电路设备或功能模块,需要实现输入处理、设备状态更新和输出显示等多个功能模块。

2.3难度

  • 中等偏难:题目要求较高的面向对象设计能力,需要理解不同设备的工作机制,并在程序中准确实现。
  • 复杂性:题目涉及多个设备的交互和电路连接的模拟,增加了实现的复杂性。
  • 细节处理:需要注意输入解析、设备状态更新和输出格式化等细节问题。

3.家居强电电路模拟程序2

3.1知识点

(1)面向对象编程:

  • 类设计与继承:题目要求设计多个类来模拟不同类型的电路设备。需要使用面向对象的设计原则,包括抽象类和接口的使用。
  • 多态与封装:利用多态和封装来实现设备的多样性和行为的封装。

(2)电路模拟:

  • 设备类型:包括开关、分档调速器、连续调速器、白炽灯、日光灯、吊扇和新增的落地扇。

  • 设备特性:每种设备有特定的工作原理和特性,例如开关的状态、调速器的档位、灯的亮度和风扇的转速。

  • 电阻与电流:本次迭代引入了设备的电阻,需要考虑电阻对电路电流的影响。

(3)电路结构:

  • 串联与并联电路:需要模拟串联和并联电路的连接方式,理解电压和电流在不同电路结构中的分布。

  • 电路拓扑:处理复杂的电路拓扑,包括串联和并联电路的组合。

(4)输入解析与处理:

  • 多种输入格式:包括设备信息、连接信息、控制设备调节信息、电路信息等。

  • 正则表达式与字符串处理:使用正则表达式和字符串解析技术处理复杂的输入格式。

3.2题量

  需要实现多个类,每个类代表一种设备或功能模块,实现输入处理模块、设备状态更新模块、电路模拟模块和输出显示模块,每个模块需要处理特定的任务,确保整体系统的正确性和效率。

3.3难度

(1)复杂性:

  • 中等偏难:题目涉及多个设备的交互和电路连接的模拟,增加了实现的复杂性。

  • 细节处理:需要注意输入解析、设备状态更新和输出格式化等细节问题。

(2)电路模拟难度:

  • 需要理解并实现电路中电压、电流和电阻的关系,尤其是在处理并联和串联电路时。

  • 设备的电阻引入了对电流的计算,增加了电路模拟的难度。

(3)输入输出处理:

  • 复杂的输入格式和输出要求,需要精确的解析和格式化技术。

  • 需要确保输入信息的正确解析和设备状态的准确输出。

二、设计与分析

1.答题判题程序4

1.1类的分析与设计

(1)类的分析

InputFormatException类:自定义异常类,用于在输入格式错误时抛出异常。

Question类:抽象基类,表示题目。包含题目的基本信息(编号、内容、标准答案)和一个抽象方法judgeAnswer,用于判定答案的正确性。

SingleChoiceQuestion类:继承自Question,表示计算题。实现了judgeAnswer方法,用于判定单选题答案的正确性。

MultipleChoiceQuestion类:继承自Question,表示多选题。实现了judgeAnswer方法,用于判定多选题答案的正确性,包括完全正确、部分正确和错误的判断。

FillinTheBlankQuestion类:继承自Question,表示填空题。实现了judgeAnswer方法,用于判定填空题答案的正确性,包括完全正确、部分正确和错误的判断。

QuestionBank类:题库类,管理所有题目。提供添加题目和根据题目编号获取题目的功能。

Paper类:试卷类,表示一份试卷。包含试卷编号、总分、每个题目的分值和被标记为删除的题目。提供添加题目、标记题目为删除、计算总分等功能。

PaperBank类:试卷库类,管理所有试卷。提供添加试卷和根据试卷编号获取试卷的功能。

Student类:学生类,表示一个学生。包含学生的学号和姓名。

StudentBank类:学生库类,管理所有学生。提供添加学生和根据学号获取学生的功能。

AnswerPaper类:答卷类,表示一份答卷。包含关联的试卷、学生学号、答案映射、判题结果列表、得分列表和总得分。提供添加答案、保存判题结果、计算得分和总得分等功能。实现了Comparable接口,用于按学生学号和试卷编号排序。

AnswerPaperBank类:答卷库类,管理所有答卷。提供添加答卷和对答卷进行排序的功能。

InputHandler类:输入处理类,负责读取和解析输入的数据,并存储到相应的数据结构中。处理题目、试卷、学生、答卷的输入,以及格式错误的处理。

OutputHandler类:输出处理类,负责输出各种信息,包括格式错误、试卷总分警示、答题信息、学生得分信息、试卷无效信息等。

Main类:主类,程序的入口。创建各个类的实例,处理输入、判题、计算得分,并输出结果。

(2)类的设计

1.2SourceMonitor报表分析

(1)代码规模:

  • 总行数:638行,这表明代码文件相对较大。

  • 语句数:321个,相对于行数来说,代码量适中。

(2)代码复杂性:

  • 最大复杂度:12,这是一个相当高的值,表明存在至少一个方法的逻辑非常复杂。

  • 平均复杂度:2.06,表明整体上方法的平均复杂度是可以接受的,但仍有一些方法的复杂度较高。

  • 最复杂方法:MultipleChoiceQuestion.judgeAnswer(),复杂度为12,这可能意味着该方法的逻辑过于复杂,需要重构以降低复杂度。

(3)代码结构:

  • 最大复杂度:12,这是一个相当高的值,表明存在至少一个方法的逻辑非常复杂。

  • 平均复杂度:2.06,表明整体上方法的平均复杂度是可以接受的,但仍有一些方法的复杂度较高。

  • 最复杂方法:MultipleChoiceQuestion.judgeAnswer(),复杂度为12,这可能意味着该方法的逻辑过于复杂,需要重构以降低复杂度。

(4)代码注释:

  • 含注释行的百分比:18.8%,这个比例相对较高,表明代码的注释较为充分,有助于提高代码的可读性。

(5)代码块深度:

  • 最大代码块深度:7,这是一个相当高的值,表明代码中存在较深的嵌套结构,这可能增加代码的理解和维护难度。

  • 平均代码块深度:2.08,这个值相对较高,表明代码中存在一定程度的嵌套,但整体上仍然可控。

(6)分支和方法调用:

  • 分支语句百分比:19.3%,这个比例相对较低,表明代码中的分支逻辑不多,这有助于减少代码的复杂性。

  • 方法调用语句数:184个,相对于语句总数来说,这是一个较高的比例,表明代码中方法调用频繁,这可能影响代码的耦合度和性能。

(7)最复杂方法分析:

  • FillInTheBlankQuestion.judgeAnswer():复杂度为6,语句数为9,最大深度为7,调用数为6,这表明该方法可能也需要关注和可能的重构。

1.3主要代码分析

(1)处理计算题信息的输入的代码

 1 private void handleSingleChoice(String line, QuestionBank questionBank) throws InputFormatException {
 2         Pattern pattern = Pattern.compile("#N:(\\d+)\\s+#Q:(.*?)\\s+#A:(.*)");
 3         Matcher matcher = pattern.matcher(line);
 4         if (matcher.find()) {
 5             int questionNumber = Integer.parseInt(matcher.group(1).trim());
 6             String content = matcher.group(2).trim();
 7             String standardAnswer = matcher.group(3).trim();
 8             questionBank.addQuestion(new SingleChoiceQuestion(questionNumber, content, standardAnswer));
 9         } else {
10             throw new InputFormatException("wrong format:" + line);
11         }
12     }
handleSingleChoice

功能:处理计算题的输入,将其解析并存储到题库中。

逻辑:

  • 使用正则表达式匹配输入行,提取题目编号、内容和标准答案。

  • 如果匹配成功,创建一个SingleChoiceQuestion对象,并添加到QuestionBank

  • 如果匹配失败,抛出InputFormatException,记录格式错误。

(2)处理试卷信息的输入的代码

 1 private void handlePaper(String line, PaperBank paperBank) throws InputFormatException {
 2         Pattern pattern = Pattern.compile("#T:(\\d+)(\\s+\\d+-\\d+)+");
 3         Matcher matcher = pattern.matcher(line);
 4         if (matcher.find()) {
 5             int paperNumber = Integer.parseInt(matcher.group(1).trim());
 6             Paper paper = new Paper(paperNumber);
 7             addQuestionsToPaper(line, paper);
 8             paper.computeTotalScore();
 9             paperBank.addPaper(paper);
10         } else {
11             throw new InputFormatException("wrong format:" + line);
12         }
13     }
handlePaper

功能:处理试卷的输入,解析题目和分值,并计算总分。

逻辑:

  • 使用正则表达式匹配输入行,提取试卷编号和题目-分值对。

  • 如果匹配成功,创建一个Paper对象。

  • 调用addQuestionsToPaper方法,将题目和分值添加到试卷中。

  • 计算试卷总分,并将试卷添加到PaperBank

  • 如果匹配失败,抛出InputFormatException,记录格式错误。

(3)答卷类的保存判题结果的代码

 1 // 根据题库判定答案的正确性
 2     public void saveResults(QuestionBank questionBank) {
 3         if (paper == null) {
 4             return; // 如果试卷无效,跳过判题
 5         }
 6         List<Integer> questionNumbers = new ArrayList<>(paper.getQuestionScores().keySet());
 7         for (int i = 0; i < questionNumbers.size(); i++) {
 8             Integer questionNumber = questionNumbers.get(i);
 9             String answer = answerMap.get(i + 1); // 试卷中的题目序号从1开始
10             Question question = questionBank.getQuestion(questionNumber);
11 
12             if (question == null) {
13                 resultList.add("false"); // 题目不存在
14             } else if (paper.isDeleted(questionNumber)) {
15                 resultList.add("invalid"); // 题目被标记为删除
16             } else {
17                 resultList.add((answer != null) ? question.judgeAnswer(answer) : "false");
18             }
19         }
20     }
saveResults

功能:根据题库判定答案的正确性,并保存判题结果。

逻辑:

  • 检查关联的Paper对象是否为null,如果是,则跳过判题。

  • 获取试卷中的题目编号列表。

  • 对于每个题目编号,获取对应的Question对象和学生的答案。

  • 如果题目不存在或被标记为删除,记录相应的结果("false"或"invalid")。

  • 否则,调用Question.judgeAnswer方法判定答案的正确性,并保存结果。

2.家居强电电路模拟程序1

2.1类的分析与设计

(1)类的分析

CircuitDevice类:抽象类,作为所有电路设备的基类,定义了公共属性和方法。

ControlDevice类:抽象类,继承自CircuitDevice,作为所有控制设备的基类,进一步抽象出控制设备的特性。

Switch类:实现一个开关设备,能够切换状态并影响电路的电压传递。

StepSpeedGovernor类:实现一个分档调速器,通过不同级别控制输出电压。

ContinuousSpeedGovernor类:实现一个连续调速器,允许设置一个连续的调速值来控制输出电压。

ControlledDevice类:继承自CircuitDevice,作为所有受控设备的基类,进一步抽象出受控设备的特性。

Lamp类:继承自ControlledDevice,作为灯具设备的基类,定义了灯具特有的计算亮度的方法。

IncandescentLamp类:实现一个白炽灯设备,根据输入电压计算亮度。

FluorescentLamp类:实现一个日光灯设备,亮度计算与白炽灯不同。

CeilingFan类:实现一个吊扇设备,根据输入电压计算转速。

SeriesCircuit类:表示整个电路系统,管理设备和连接。

InputHandler类:处理用户输入,配置电路系统。

OutputHandler类:负责输出电路的状态信息。

Main类:程序的入口,协调整个系统的运行。

(2)类的设计

2.2SourceMonitor报表分析

(1)代码规模和复杂性:

  • 总行数:463行,这是一个中等规模的代码文件。

  • 语句数:222个,相对于行数来说,代码量适中。

  • 复杂度:最大复杂度为4,平均复杂度为1.50,表明代码整体复杂度可控,但存在一些较为复杂的方法。

(2)代码结构:

  • 类和接口数:4个,表明代码被组织成了几个模块,有利于维护和扩展。

  • 每个类的方法数:平均每个类有10个方法,这表明每个类可能承担了较多的职责,可能需要进一步分析是否违反了单一职责原则。

  • 每个方法的平均语句数:5.40个,这表明方法的平均大小适中,有利于代码的可读性和可维护性。

(3)代码复杂度分布:

  • 最复杂方法:IncandescentLamp.computeBrightness()方法的复杂度最高,达到了4,这可能意味着该方法承担了过多的职责或者逻辑过于复杂,可能需要重构以降低复杂度。

  • 方法调用:方法调用语句数为90,相对于语句总数来说,这是一个较高的比例,表明代码中方法调用频繁,这可能影响性能,也可能意味着代码的耦合度较高。

(4)代码注释:

  • 含注释行的百分比:12.1%,这个比例相对较低,意味着代码的可读性和可维护性有待提高。

(5)代码块深度:

  • 平均代码块深度:1.20,这表明代码的嵌套结构相对简单,有利于代码的理解和维护。

  • 深度分布:大部分代码块深度集中在0到2之间,深度为3和4的代码块较少,这表明代码结构相对扁平,有利于减少理解代码的难度。

(6)分支和方法调用:

  • 分支语句百分比:21.2%,这个比例表明代码中包含一定数量的分支逻辑,这可能影响代码的测试和维护。

  • 方法调用语句数:90个,这可能意味着代码中存在较多的函数调用,这可能影响代码的性能和可读性。

  综上所述,Main.java文件的代码质量整体上是可控的,但存在一些可以改进的地方,特别是在代码注释、方法复杂度和方法调用方面。建议对最复杂的方法进行重构,增加代码注释,以及考虑优化方法调用以提高代码的可读性、可维护性和性能。

2.3主要代码分析

(1)给电路施加电压并且更新所有设备状态的代码

 1 public void applyVoltage() {
 2         // 检查是否有任何开关处于打开状态
 3         boolean isAnySwitchOpen = devices.values().stream()
 4             .filter(device -> device instanceof Switch)
 5             .map(device -> (Switch) device)
 6             .anyMatch(switchDevice -> switchDevice.toString().equals("turned on"));
 7 
 8         if (isAnySwitchOpen) {
 9             // 如果有任何开关是打开的,将所有设备的输入电压设置为0
10             devices.values().forEach(device -> device.setInputVoltage(0));
11         } 
12         else {
13             // 设置与VCC和GND连接的设备的电压
14             for (String[] connection : connections) {
15                 String fromDeviceId = connection[0].split("-")[0];
16                 String toDeviceId = connection[1].split("-")[0];
17 
18                 if (fromDeviceId.equals("VCC")) {
19                     CircuitDevice toDevice = devices.get(toDeviceId);
20                     toDevice.setInputVoltage(220);
21                     // 更新 toDevice 的输出
22                     updateDeviceOutput(toDevice);
23                 } else if (toDeviceId.equals("GND")) {
24                     CircuitDevice fromDevice = devices.get(fromDeviceId);
25                     fromDevice.setOutputVoltage(0);
26                     // 更新 fromDevice 的输出
27                     updateDeviceOutput(fromDevice);
28                 }
29                 else {
30                     CircuitDevice fromDevice = devices.get(fromDeviceId);
31                     CircuitDevice toDevice = devices.get(toDeviceId);
32 
33                     // 更新 fromDevice 的输出
34                     updateDeviceOutput(fromDevice);
35 
36                     // 设置 toDevice 的输入电压为 fromDevice 的输出电压
37                     toDevice.setInputVoltage(fromDevice.getOutputVoltage());
38 
39                     // 更新 toDevice 的输出
40                     updateDeviceOutput(toDevice);
41                 }
42             }
43         }
44     }
applyVoltage

功能:给串联电路施加电压,并且把所有设备的输入输出电压更新,计算出受控设备的亮度,转速等参数。

逻辑:

 

1)检查开关状态:

 

 

  • 使用Java Stream API遍历devices集合,筛选出所有Switch类型的设备。

  • 将这些设备转换为Switch对象并检查其状态。

  • 使用anyMatch方法检查是否有开关处于打开状态(toString().equals("turned on"))。

  • 如果有任何开关是打开的,则设置所有设备的输入电压为0。这是因为在实际电路中,任何开关的打开都会断开电路,阻止电流流动。

2)设置电压:

  • 如果所有开关闭合,遍历connections列表以设置设备的电压。

  • 对于每个连接,根据连接的起点和终点设备ID,执行以下操作:

    • 如果起点设备是VCC,将连接的终点设备的输入电压设置为220伏特,模拟电源连接。

    • 如果终点设备是GND,将连接的起点设备的输出电压设置为0伏特,模拟接地。

    • 对于其他设备,设置终点设备的输入电压为起点设备的输出电压,模拟电流的流动。

3)更新设备输出:

  • 调用updateDeviceOutput()方法更新每个设备的输出状态。

  • 该方法根据设备类型调用相应的方法(如updateOutput()computeBrightness()),确保设备根据其类型和当前输入电压正确更新其输出状态。

(2)更新设备的输出状态的代码

 1 private void updateDeviceOutput(CircuitDevice device) {
 2         if (device instanceof Switch) {
 3             ((Switch) device).updateOutput();
 4         } else if (device instanceof StepSpeedGovernor) {
 5             ((StepSpeedGovernor) device).updateOutput();
 6         } else if (device instanceof ContinuousSpeedGovernor) {
 7             ((ContinuousSpeedGovernor) device).updateOutput();
 8         } else if (device instanceof IncandescentLamp) {
 9             ((IncandescentLamp) device).computeBrightness();
10         } else if (device instanceof FluorescentLamp) {
11             ((FluorescentLamp) device).computeBrightness();
12         } else if (device instanceof CeilingFan) {
13             ((CeilingFan) device).computeSpeed();
14         }
15     }
updateDeviceOutput

功能:确保每个设备根据其类型和当前输入电压正确更新其输出状态。

逻辑:

1)设备类型检查:

  • 使用instanceof关键字检查设备的具体类型。

  • 根据设备类型,调用相应的更新方法:

    • 对于SwitchStepSpeedGovernorContinuousSpeedGovernor,调用updateOutput()方法。

    • 对于IncandescentLampFluorescentLamp,调用computeBrightness()方法。

    • 对于CeilingFan,调用computeSpeed()方法。

2)更新设备状态:

  • updateOutput()方法用于更新控制设备的输出电压。

  • computeBrightness()computeSpeed()方法用于计算受控设备的亮度和速度。

  • 这些方法根据设备的输入电压和内部状态(如开关状态或调速级别)计算并设置设备的输出状态。

(3)处理输入信息的代码

 1 public void processInput(String input) {
 2         if (input.startsWith("[")) {
 3             // 处理连接
 4             Pattern pattern = Pattern.compile("\\[(.*?)\\]");
 5             Matcher matcher = pattern.matcher(input);
 6             if (matcher.find()) {
 7                 String[] parts = matcher.group(1).split(" ");
 8                 circuit.addConnection(parts);
 9                 // 确保设备实例存在
10                 for (String part : parts) {
11                     String[] deviceInfo = part.split("-");
12                     String deviceId = deviceInfo[0];
13                     if (circuit.getDevice(deviceId) == null) {
14                         addDeviceById(deviceId);
15                     }
16                 }
17             }
18         } else if (input.startsWith("#")) {
19             // 处理设备命令
20             if (input.matches("#K\\d+")) {
21                 String id = input.substring(1);
22                 Switch sw = (Switch) circuit.getDevice(id);
23                 if (sw != null) {
24                     sw.toggle();
25                 }
26             } else if (input.matches("#F\\d+\\+")) {
27                 String id = input.substring(1, input.length() - 1);
28                 StepSpeedGovernor controller = (StepSpeedGovernor) circuit.getDevice(id);
29                 if (controller != null) {
30                     controller.increaseLevel();
31                 }
32             } else if (input.matches("#F\\d+-")) {
33                 String id = input.substring(1, input.length() - 1);
34                 StepSpeedGovernor controller = (StepSpeedGovernor) circuit.getDevice(id);
35                 if (controller != null) {
36                     controller.decreaseLevel();
37                 }
38             } else if (input.matches("#L\\d+:\\d+\\.\\d{2}")) {
39                 Pattern pattern = Pattern.compile("#L(\\d+):(\\d+\\.\\d{2})");
40                 Matcher matcher = pattern.matcher(input);
41                 if (matcher.find()) {
42                     String id = "L" + matcher.group(1);
43                     double setting = Double.parseDouble(matcher.group(2));
44                     ContinuousSpeedGovernor controller = (ContinuousSpeedGovernor) circuit.getDevice(id);
45                     if (controller != null) {
46                         controller.setSetting(setting);
47                     }
48                 }
49             }
50         }
51     }
processInput

功能:解析用户输入,动态配置电路的连接和设备状态,通过命令解析和设备操作,模拟用户对电路的控制和配置。

逻辑:

1)命令解析:

  • 检查输入行的开头字符以确定命令类型。

  • 如果输入以[开头,表示连接命令,使用正则表达式解析连接和设备ID。

  • 如果输入以#开头,表示设备命令,解析并执行相应的设备操作(如切换开关、调整调速器)。

2)处理连接命令:

  • 使用正则表达式提取连接信息。

  • 将连接信息添加到connections列表。

  • 确保连接中涉及的每个设备已实例化并添加到电路中。

3)处理设备命令:

  • 根据命令类型(如#K#F#L),调用相应的设备方法(如toggle()increaseLevel()setSetting())。

  • 确保设备实例存在于电路中。

3.家居强电电路模拟程序2

3.1类的分析与设计

(1)类的分析

CircuitDevice类:这是一个抽象基类,表示电路中的基本设备。它定义了所有电路设备的共有属性和方法,如设备标识符、电压、电阻和电流等。

ControlDevice类:继承自CircuitDevice,作为所有控制设备的抽象基类。定义了一个抽象方法updateOutput(),要求所有子类实现该方法,用于更新设备的输出状态。

Switch类:继承自ControlDevice,表示电路中的开关设备。

StepSpeedGovernor类:继承自ControlDevice,表示分档调速器。

ContinuousSpeedGovernor类:继承自 ControlDevice,表示连续调速器。

ControlledDevice类:继承自CircuitDevice,作为所有受控设备的抽象基类。

Lamp类:继承自ControlledDevice,作为所有灯类的抽象基类。定义了一个抽象方法computeBrightness(),要求所有子类实现该方法,用于计算灯的亮度。

IncandescentLamp类:继承自Lamp,表示白炽灯。

FluorescentLamp类:继承自 Lamp,表示日光灯。

CeilingFan类:继承自ControlledDevice,表示吊扇。

FloorFan类:继承自ControlledDevice,表示落地扇。

SeriesCircuit类:继承自CircuitDevice,表示一个串联电路。

ParallelCircuit类:继承自 CircuitDevice,表示一个并联电路。

InputHandler类:处理输入命令以配置电路。

OutputHandler类:负责输出电路中所有设备的状态。

Main类:程序的入口,负责读取输入、处理电路配置和输出设备状态。

(2)类的设计

 

3.2SourceMonitor报表分析

(1)代码规模

  • 总行数:814行,表明代码文件相对较大。

  • 语句数:318个,相对于行数来说,代码量适中。

(2)代码复杂性:

  • 最大复杂度:10,这是一个相当高的值,表明存在至少一个方法的逻辑非常复杂。

  • 平均复杂度:1.57,表明整体上方法的平均复杂度是可以接受的,但仍有一些方法的复杂度较高。

  • 最复杂方法processControlCommand().addDeviceById(),复杂度为10,这可能意味着该方法的逻辑过于复杂,需要重构以降低复杂度。

(3)代码结构:

  • 类和接口数:14个,表明代码被组织成了多个模块,这有助于维护和扩展。

  • 每个类的方法数:平均每个类有2.64个方法,这个数字相对较低,可能意味着每个类的功能较为集中。

  • 每个方法的平均语句数:7.35个,这表明方法的平均大小适中,有利于代码的可读性和可维护性。

(4)代码注释

 

  • 含注释行的百分比:28.1%,这个比例相对较高,表明代码的注释较为充分,有助于提高代码的可读性。

(5)代码块深度:

  • 最大代码块深度:5,这是一个相对较低的值,表明代码中存在一定程度的嵌套,但整体上仍然可控。

  • 平均代码块深度:1.32,这个值相对较低,表明代码的嵌套结构相对简单,有利于减少理解代码的难度。

(6)分支和方法调用:

  • 分支语句百分比:20.8%,这个比例表明代码中的分支逻辑适中,这有助于增加代码的灵活性,但也可能增加代码的复杂性。

  • 方法调用语句数:132个,相对于语句总数来说,这是一个较高的比例,表明代码中方法调用频繁,这可能影响代码的耦合度和性能。

(7)最复杂方法分析:

  • processControlCommand().addDeviceById():复杂度为10,语句数为19,最大深度为3,调用数为10,这表明该方法可能涉及到多个分支和调用,需要关注和可能的重构。

  • 其他一些方法如Switch.updateOutput()StepSpeedGovernor.increaseLevel()的复杂度也为3,可能也需要关注。

  综上所述,Main.java文件的代码质量整体上是可控的,但存在一些可以改进的地方,特别是在代码复杂度和方法调用方面。建议对最复杂的方法进行重构,以降低复杂度和深度,同时保持较高的注释水平以提高代码的可读性。此外,考虑优化方法调用以减少耦合度和提高性能。

3.3主要代码分析

(1)计算串联电路电阻的代码

 1 public void computeResistance() {
 2         double sumR = 0;
 3         // 检查是否有任何开关处于打开状态
 4         boolean isAnySwitchOpen = devices.values().stream()
 5                 .filter(device -> device instanceof Switch)
 6                 .map(device -> (Switch) device)
 7                 .anyMatch(switchDevice -> switchDevice.toString().equals("turned on"));
 8 
 9         if (isAnySwitchOpen) {
10             // 如果有任何开关是打开的,则表明串联电路断路电阻为无穷大电压差为0
11             sumR =  Double.POSITIVE_INFINITY;
12         } else {//如果开关全部闭合了,在计算串联电阻的总阻值时,遇到是并联电路也要先计算一下并联电路的总电阻
13             for (Map.Entry<String, CircuitDevice> entry : devices.entrySet()) {//遍历电器设备的HashMap
14                 if(entry.getValue() instanceof StepSpeedGovernor || entry.getValue() instanceof ContinuousSpeedGovernor)
15                     continue;//如果是调速器那么跳过,不加上调速器的电阻
16                 if(entry.getValue() instanceof ParallelCircuit) {
17                     ((ParallelCircuit)entry.getValue()).computeResistance();
18                 }
19                 sumR += entry.getValue().getResistance();
20             }
21         }
22         setResistance(sumR);
23     }
computeResistance

功能:computeResistance()方法用于计算串联电路的总电阻。

逻辑:

  • 判断是否有开关打开: 代码首先通过流式处理检查串联电路中是否存在任何一个开关处于打开状态(即开关的状态为 "turned on")。如果有开关打开,整个电路的电阻将被设定为无穷大,表示电路断开,无法通过电流。
  • 处理开关断开: 如果有任何开关打开,方法会立即返回,电路的电阻设置为无穷大,表明串联电路断路,不会继续计算电阻。
  • 计算电阻:
    • 如果所有开关都闭合,电路是通电的。接着方法会遍历所有设备,累加它们的电阻值。
    • 如果设备是调速器(StepSpeedGovernorContinuousSpeedGovernor),它们的电阻会被忽略,因为调速器并不直接影响电阻的计算。
    • 如果设备是并联电路(ParallelCircuit),则会递归调用computeResistance()方法来计算并联电路中的电阻。

(2)串联电路分配电压的代码

 

 1 public void applyVoltage() {
 2         double totalVoltage = getVoltageDifference();//串联电路的总电压差
 3         
 4         // 检查是否存在调速器,并计算其输出电压
 5         for (CircuitDevice device : devices.values()) {
 6             if (device instanceof StepSpeedGovernor) {
 7                 device.setInputVoltage(totalVoltage);
 8                 ((StepSpeedGovernor) device).updateOutput();
 9                 totalVoltage = device.getOutputVoltage(); // 使用调速器的输出电压
10                 break; // 假设只有一个调速器,找到后即可退出
11             } else if (device instanceof ContinuousSpeedGovernor) {
12                 device.setInputVoltage(totalVoltage);
13                 ((ContinuousSpeedGovernor) device).updateOutput();
14                 totalVoltage = device.getOutputVoltage(); // 使用调速器的输出电压
15                 break; // 假设只有一个调速器,找到后即可退出
16             }
17         }
18         
19         for (Map.Entry<String, CircuitDevice> entry : devices.entrySet()) {
20             CircuitDevice device = entry.getValue();
21             
22             if (device instanceof StepSpeedGovernor || device instanceof ContinuousSpeedGovernor) {
23                 continue; // 跳过调速器
24             }
25 
26             if(Double.isInfinite(device.getResistance())) {//如果这个设备的电阻无穷大,则等于输入的电压差
27                 device.setVoltageDifference(totalVoltage);
28             }
29             else {//如果电阻不是无穷大,按照分压法给电器设备分压
30                 double deviceVoltage = totalVoltage * device.getResistance() / getResistance();
31                 device.setVoltageDifference(deviceVoltage);
32             }
33 
34             if(device instanceof ParallelCircuit) {
35                 ((ParallelCircuit) device).applyVoltage();
36             }
37 
38         }
39 }
View Code

 

功能:applyVoltage()方法负责根据电路的电阻分配电压。它应用于串联电路的每个设备,分配合适的电压。

逻辑:

 

  • 总电压: 该方法使用电路的总电压差(VoltageDifference)开始进行电压分配。

 

  • 处理调速器:
    • 在给设备分配电压之前,方法会首先检查是否存在调速器。如果存在调速器(StepSpeedGovernorContinuousSpeedGovernor),则会先计算其输出电压。调速器的输出电压是输入电压的某个比例,因此电压会根据调速器的输出逐步传递给其他设备。
  • 分配电压给其他设备:
    • 对于其他设备,电压会按照设备的电阻进行分配。使用分压法(totalVoltage * deviceResistance / totalResistance)来计算每个设备的电压差。
    • 如果设备的电阻为无穷大(如断路),则不再分配电压。
  • 递归调用: 如果设备是并联电路(ParallelCircuit),该方法会递归调用并联电路的applyVoltage()方法,确保每个并联电路中的设备都能得到适当的电压。

 

 

三、踩坑心得

1.答题判题程序4

1.1输入信息的解析

  (1)在解析输入信息时,需要注意对于填空题的答卷的答案判断正确与否时,如果内容与标答一致就判为全对,即使是在标答前后都加了空格的答案也算是全对,同理,对于部分正确的结果,如果是前后加了空格的也判为部分正确。

  测试样例:

  错误输出:

  正确输出:

 

  (2)在解析输入信息时,要注意最后一个答案要如果有空格,一定要去除最后一个空格,并且不能用trim(),因为如果用trim()的话,当答案是空格时,就会把答案变成空的,在输出时不会把空格都输出来。

2.家居强电电路模拟程序1

2.1施加电压的处理过程

  在这道题的题目中虽然说是根据电压差来计算亮度或者是速度,但是由于题目只是一条串联电路并且电路上只有一个受控设备,所以我就用受控设备的输入电压的值当作电压差来计算受控设备的亮度或者是速度。因此在施加电压的过程中,我是通过遍历连接关系,将前一个设备的输出电压当作后一个设备的输入电压,并且如果是与VCC相连的设备输入电压设为220V,与GND相连的设备的输出电压是0V,但是由于我在将前一个设备的输出电压传递到后一个设备的输入电压后没有更新后一个设备的输出,所以导致受控设备没有进行计算亮度或者速度这一步,因此,输出时,受控设备的状态还是初始值0。

  解决办法:在applyVoltage方法中电压传递完之后,再更新后一个设备的输出状态,如果是控制设备则更新输出电压,如果是受控设备则调用相应的计算亮度或速度的方法。

  测试样例:

   错误输出:

   正确输出:

2.2考虑断路情况

  起初我我只考虑了当开关在受控设备之前,如果开关断开,也就是在受控设备之前出现断路的话,由于电压传递,那么受控设备的输入电压就会为0,受控设备的亮度或者速度也就会为0,但是忽略了当受控设备在开关之前,而开关断开的情况,如果是电压传递的话,由于电压传递受控设备的输入电压就不为0,受控设备的亮度或者速度也就不会为0,不符合断路时受控设备的状态。

  解决办法:修改applyVoltage方法,在遍历连接关系之前,先检查是否有开关打开,如果有开关打开那么就将电路中所有设备的输入电压设置为0,如果开关都闭合了,那么再进行电压传递,更新设备状态。

  测试样例:

 

   错误输出:

  正确输出:

 

3.家居强电电路模拟程序2

3.1电压分配

  在串联电路中用分压法给电路中各个电器设备分配电压时,要注意,如果电路中有调速器,那么串联电路除调速器外的所有电器设备的总电压差就等于调速器的输出电压,因为在题目中调速器最多只有一个,并且与VCC直连,所以调速器的输入电压就等于总的串联电路的输入电压。

  由于在串联电路中我的设备是用HashMap存储的,一开始我是在遍历HashMap的时候如果遇到了调速器就将总的电压差变为调速器的输出电压,但是HashMap是无序的,所以不能保证调速器就是第一个遍历到的设备,这就会导致电器设备电压分配错误。

  解决方法:在遍历HashMap为电路中的电器设备分配电压之前,先遍历一遍HashMap,如果有调速器则把除调速器外的所有电器设备的总电压差改为调速器的输出电压。

  测试样例:

  错误输出:

  正确输出:

四、改进建议

1.答题判题程序4

(1)代码结构与可读性:

  • 代码注释:虽然代码已经有一些注释,但可以更详细地描述复杂部分的逻辑,尤其是在judgeAnswer方法中。
  • 方法分解InputHandler中的handleInput方法较长,可以进一步分解成更小的方法,以提高可读性和维护性。

(2)异常处理:

  • 异常处理一致性:在InputHandler中,对于格式错误的处理可以统一使用异常机制,而不是有些地方使用异常,有些地方直接输出错误信息。这样可以保持代码风格的一致性。

(3)性能优化:

  • 流操作:在Paper.computeTotalScoreAnswerPaper.computeAnsTotalScore中使用流操作计算总分时,可以考虑直接使用循环来提高性能,尤其是在数据量较大时。

(4)代码的健壮性:

  • 判题逻辑健壮性:在judgeAnswer方法中,考虑对输入进行更严格的验证,确保输入格式正确。例如,检查答案是否包含非法字符或格式错误。

(5)代码复用:

  • 正则表达式复用:在InputHandler中,正则表达式被多次重复定义。可以将这些正则表达式提取为类的常量,以便复用和维护。

2.家居强电电路模拟程序1

(1)设计改进:

  • 使用 Enum 优化开关状态管理:Switch 类中的 isClosed 状态用 enum 来表示,避免使用魔法数字 (01),使代码更具可读性和清晰的状态表达。

  • 设备类型的扩展性:使用工厂模式或设备注册机制,避免硬编码设备类型,增强代码的灵活性。通过工厂模式解耦设备的创建,方便将来增加新设备时不需要修改现有代码。

(2)代码可读性和可维护性:

  • 拆分 applyVoltage 方法:applyVoltage 方法拆分为多个职责单一的小方法,例如,处理开关的状态、应用电压到设备等。这样可以降低 applyVoltage 方法的复杂度,提升代码的可读性和易维护性。

  • 将亮度计算和状态更新分开:将灯具类中的亮度计算逻辑和状态更新逻辑分开,使得 Lamp 类更加专注于行为,而不需要承担过多的职责,增强代码的单一职责原则(SRP)。

(3)错误处理和健壮性:

  • 加强错误处理:在处理设备ID和连接时加强错误检查。如果遇到无效的设备ID、连接错误等,应该及时抛出异常并给出明确的错误信息,避免程序在异常情况下继续执行。

3.家居强电电路模拟程序2

(1)代码组织与结构优化:

  • 增强类的职责分明性:可以使用设计模式中的责任链模式策略模式将不同的职责分离。比如电压分配、设备更新等操作可以分配到独立的类或模块中,这样有助于保持每个类的单一职责,减少类之间的耦合。

  • 增强类的封装性:可以对电器设备类进行封装,例如将 resistancevoltage 等私有化,不直接暴露设置方法,而是提供封装好的操作接口。例如,使用保护方法来控制外部对这些敏感属性的访问。

  • 简化设备管理:考虑采用工厂模式来简化设备的创建与管理,避免 addDeviceById 中的多次判断与代码重复。通过工厂方法来统一创建和添加设备,确保添加设备的操作一致性。

  • 改进电压计算与分配的逻辑:将电压分配的逻辑提取到单独的电压分配器类(例如 VoltageDistributor),并通过策略模式来处理不同设备类型的电压分配策略。

(2)性能优化:

  • 避免重复计算:可以通过缓存机制来避免重复计算。可以在电路状态发生变化时标记电路的状态,然后只在需要重新计算时执行相关操作。

  • 避免多次迭代和递归:考虑使用缓存(比如Map<String, Double> deviceVoltageCache)来存储每个设备的电压和电阻结果,避免每次都重新计算,尤其是对于重复的计算过程。

(3)代码可维护性与可读性:

  • 减少魔法数字:将这些常量提取为类的静态常量,增加代码的可读性与可维护性。如果这些常量有明确的物理意义,最好给它们加上注释,或者提取成枚举类型。

  • 改进异常处理:可以为设备添加操作引入异常处理机制,并且在关键操作(如电压分配、设备连接)中加入适当的异常抛出和捕获。

  • 优化正则表达式的使用:优化正则表达式的使用,确保每个正则表达式的匹配和捕获规则都清晰,并添加异常处理,避免输入格式错误时出现问题。

五、总结

1.心得体会

  在完成答题判题程序的设计和实现过程中,我收获了很多宝贵的经验。首先,我深刻认识到程序设计中的逻辑严密性和细节处理的重要性。答题判题程序看似简单,但在实际操作中却充满了挑战,尤其是在处理不同题型和评分规则时。通过对输入数据的严格验证,我明白了如何确保程序的健壮性,避免因无效输入或格式错误导致程序崩溃或者结果不准确。其次,如何高效地设计判题逻辑和评分标准也是我在过程中不断琢磨的重点。比如,选择题、填空题、编程题等多种题型的判定方法各不相同,我必须根据题型的特点设计相应的评分算法,既要确保正确性,又要考虑程序执行的效率。

  在编写程序时,我还特别注重了代码的可维护性和可扩展性。随着项目的深入,我逐渐意识到如果一开始没有做好良好的代码结构设计,后期的修改和功能扩展会变得极其困难。因此,在设计判题程序时,我尽量避免硬编码,采用了模块化的思路,尽量让每个功能模块独立且易于修改,这样不仅提升了代码的可读性,也为未来可能的功能拓展打下了基础。

  在完成这个智能家居电路模拟系统的设计过程中,我深刻体会到了如何将理论知识与实际应用相结合。首先,设计思路的明确至关重要,尤其是在处理控制设备和受控设备之间的逻辑关系时。开关、调速器等设备的行为控制让我更加了解了如何通过电压和电流的变化来驱动不同的家电设备。通过模拟开关的开合、调速器的档位变化,我发现了在不同设备连接情况下,如何准确判断电流电压对设备状态的影响。这个过程让我认识到,设计一个合理的电路不仅仅是考虑功能,还要注重电路的稳定性和可靠性。尤其在多设备联动时,如何保证每个元件之间的协调工作,避免误操作或短路,是一个需要精确思考的细节。

  总结来看,这两个程序虽然在实现上有所不同,但在编程过程中都让我获得了很多经验。它们都让我意识到,良好的代码结构和严谨的逻辑设计是编写高质量程序的关键。而且,在开发过程中对细节的关注、对可能出现的边界情况的考虑,以及如何根据实际需求合理设计算法和界面,都是编程中不可忽视的重要部分。通过这两个项目,我不仅提升了自己的编程能力,也更加明确了在未来的开发工作中,如何通过合理的设计和优化,不断提高程序的性能和用户体验。

2.学习计划

2.1Java语言基础与核心知识

  • 面向对象编程(OOP):加深对Java面向对象的理解,特别是多态、继承、封装、抽象类和接口的运用。在实际项目中做到合理使用这些特性,提升代码的复用性与扩展性。

  • Java内存管理与垃圾回收机制:掌握JVM内存模型,理解堆和栈的管理,深入学习垃圾回收机制(GC),并能在项目中通过优化GC配置来提高性能。

2.2Java高级特性与进阶技术

  • Lambda表达式与Stream API深入学习Java 8引入的Lambda表达式、Stream API,掌握如何利用函数式编程思想简化代码,提升代码的可读性和执行效率。

  • Java反射机制:学习Java反射的原理与使用场景,理解如何动态获取类信息、调用方法等,并掌握其在框架设计中的应用,如Spring、MyBatis等。

3.对于课程和教学的改进建议

 (1)强化代码优化与性能调优实践:  

  课程可以增加更多关于代码优化、性能调优的实战内容。包括JVM调优、数据库查询优化、分布式系统的性能监控等,帮助我们在未来的工作中避免常见的性能瓶颈。

 (2)定期开展技术分享与讨论

  除了基础知识的教学,可以组织技术分享会或讨论会,让学生参与技术趋势的讨论,分享自己的学习经验,增强我们对技术的理解和应用。这也有助于培养我们的技术思维和沟通能力。

实现一个白炽灯设备,根据输入电压计算亮度。

posted @ 2024-11-23 19:55  22207223-王颖  阅读(94)  评论(0)    收藏  举报