Java题目集4-6实现总结

前言

  本文介绍了如何使用Java设计实现答题程序的迭代,以及家居强电电路模拟程序及其迭代:

  答题判题程序-4在答题判题程序-3的基础上,新增内容包括:选择题和填空题的题目信息输入格式,其中多选题和填空题的评分规则增加了“部分正确”的判定;允许信息以任意顺序输入;处理多个学生多张试卷的情况时,按学号和试卷号升序输出成绩,同时检查并提示试卷总分是否为100分。

  智能家居强电电路模拟系统设计包括控制设备(开关、分档调速器、连续调速器)和受控设备(白炽灯、日光灯、吊扇)的模拟。系统通过输入设备信息、连接信息和控制调节信息,计算并输出各设备的状态或参数,按开关、分档调速器、连续调速器、白炽灯、日光灯、吊扇的顺序依次输出。系统支持串联电路,第一次迭代后接入了并联电路。

 

答题判题程序:

一、设计与分析

1. 类定义

(1)Question 类:

用于表示一个题目,包含题目编号 (number)、题目内容 (content) 和标准答案 (standardAnswer)。

提供了一个 judgeAnswer 方法来判断答案是否正确。

 

(2) Exam 类:

用于表示一套题目集合,包含一个题目数组 (questions) 和题目数量 (count)。

提供方法 saveQuestion 来保存题目。

提供方法 getQuestions 返回排序后的题目数组。

提供方法 getCount 获取题目数量。

 

(3)Paper 类:

用于表示一份答卷,包含一个考试对象 (exam)、答案数组 (answers) 和判断结果数组 (judgments)。

提供方法 saveAnswer 来保存答案。

提供方法 judgeAnswers 来判断答案是否正确。

提供方法 displayAnswersAndJudgments 来显示题目内容、答案和判断结果。

提供方法 outputJudgments 来输出判断结果。

 

(4)设计类图如下:

6e1b918b6799f202bfa687bb3edd870

 

2. 主程序逻辑

主函数 (main 方法):

初始化 Scanner 对象用于读取输入。

读取题目数量,并创建一个 Exam 对象。

逐行读取题目信息,并创建 Question 对象,保存到 Exam 中。

读取答题信息,并创建一个 Paper 对象。

解析答题信息,并保存到 Paper 中。

调用 judgeAnswers 方法进行答案判断。

显示答题信息和判断结果。

 

3. 迭代带来的改变:

答题判题程序-4在答题判题程序-3的基础上,新增内容和改进点如下:

(1)选择题和填空题的题目信息输入格式:

  选择题:`#Z:`题目编号` `#Q:`题目内容` `#A:`标准答案`,多选题的标准答案用空格分隔。

  填空题:`#K:`题目编号` `#Q:`题目内容` `#A:`标准答案`。

(2)多选题和填空题的评分规则:

  多选题:

    完全正确:得满分。

    部分正确:包含部分正确答案且没有错误答案:得一半分(小数部分按截尾法处理)。

         包含错误答案或完全未作答:0分。

  填空题:

    完全匹配标准答案:得满分。

    部分正确:包含部分正确答案且没有错误字符:得一半分(小数部分按截尾法处理)。

         包含错误字符或完全未作答:0分。

(3)信息输入顺序:

  允许信息以任意顺序输入,包括试卷信息、题目信息、学生答案等。

  处理多个学生多张试卷:按学号和试卷号升序输出成绩。

             检查并提示每张试卷的总分是否为100分。

这些改进使得程序更加灵活,能够处理更复杂的题目类型和多样的输入顺序,同时确保评分的准确性。

4. 代码评价:

IMG_256

根据得出的 Kiviat 图形和区块直方图,可以得出以下几点评价:

(1)代码复杂度:

平均复杂度较低,这表明代码可能较为简洁易懂,易于维护。

最大复杂度也较低,说明没有特别难以理解的部分。

(2)注释覆盖率:

评论行所占比例较高,这意味着代码中有足够的注释,有利于他人阅读和理解代码逻辑。

(3)方法和类的分布:

每个类的方法数量适中,平均每个方法的语句数也在合理范围内,这表明代码模块化较好,功能划分清晰。

(4)代码深度:

平均深度和最大深度都比较低,说明代码层次结构不深,减少了嵌套导致的理解难度。

(5)区块分布:

区块直方图显示大多数代码块的深度都在较浅的层级,只有少数达到较高的深度,这表明代码整体结构良好,不易出现过深的嵌套问题。

综上所述,这段代码看起来具有良好的可读性和可维护性,注释充分,复杂度和深度控制得当。不过,具体的评价还需要结合实际业务需求和技术规范来进行综合考量。

二、 踩坑心得

在多选题判断逻辑中,checkAnswer方法的实现存在一些问题,可能导致评分逻辑不够严谨。以下是对代码的评估和错误修改建议:

错误分析:

checkAnswer 方法返回值问题:

checkAnswer方法返回true时没有明确区分完全正确和部分正确,只返回true表示“部分正确”。如果没有包含错误答案,应该返回一个值来指示是完全正确还是部分正确。

评分逻辑未返回分数:

checkAnswer方法并没有提供评分的功能,这与需求不符。评分应该在该方法中集成。

部分正确评分未处理:

没有计算并返回部分正确的分数,只返回了一个布尔值,无法反映出正确的分数,这在实际评分中将导致问题。

修改后的逻辑:

以下是修改后的MultipleChoiceQuestion类的代码片段,增强了多选答案的判断和评分逻辑:

class MultipleChoiceQuestion extends Question {

public MultipleChoiceQuestion(String number, String content, String standardAnswer) {

super(number, content, standardAnswer);

}

// 添加评分和正确性判断的新方法

public int score(String studentAnswer, int questionScore) {

String[] correctAnswers = getStandardAnswer().split(" ");

Set<String> correctSet = new HashSet<>(Arrays.asList(correctAnswers));

String[] studentAnswers = studentAnswer.split(" ");

Set<String> studentSet = new HashSet<>(Arrays.asList(studentAnswers));

// 检查答案是否不含错误答案

if (!correctSet.containsAll(studentSet)) {

return 0; // 含有错误答案,得0分

}

// 检查是否完全正确

if (studentSet.equals(correctSet)) {

return questionScore; // 完全正确,得满分

}

// 部分正确,计算正确的数量

int correctCount = 0;

for (String ans : studentAnswers) {

if (correctSet.contains(ans)) {

correctCount++;

}

}

return (int) Math.floor((double) correctCount / correctAnswers.length * questionScore); // 按比例计算得分

}

}

  

这些修改的逻辑和实现提升了代码的质量,确保了评分系统更符合实际需求和用户体验。在编写复杂评分逻辑时,始终关注结构化和规范化的设计原则,可以帮助开发者更好地应对未来的扩展需求或逻辑更改。

三、 改进建议

1. 使用正则表达式:

在输入解析时,可以考虑使用正则表达式来匹配和提取所需的数据,从而简化代码逻辑并提高灵活性。

2. 简化学生答案的处理:

在handleNewAnswerSheet中,可以创建一个专门的方法来解析学生答案的格式,减少方法的复杂度,并提高复用性。

3. 统一的答案评判接口:

可以为题目评分逻辑创建一个接口,使得不同类型的题目(如填空题和多选题)都实现这个接口,这样将评判逻辑和题目类型解耦,提高代码的扩展性。

四、 实验总结

通过本次实验,我学到了以下几个方面:

1. 代码结构化和规范化:

通过引入统一的题目信息输入格式和评分规则,使代码更加结构化和规范化,提高了代码的可读性和可维护性。

2. 评分逻辑的准确性:

明确区分了完全正确和部分正确的情况,确保了评分逻辑的准确性,避免了因逻辑不清导致的评分错误。

3. 输入解析的简化:

使用正则表达式简化了输入解析逻辑,提高了代码的灵活性和可读性,减少了出错的可能性。

4. 代码复用性:

创建了专门的方法来处理学生答案的格式,减少了代码的冗余,提高了代码的复用性。

5. 扩展性:

为题目评分逻辑创建了统一的接口,使得不同类型的题目都能实现该接口,提高了代码的扩展性,方便未来增加新的题目类型。

 

家居强电电路模拟程序:

一、 设计与分析

1. 类定义

(1)Electric类:

该类是所有电气设备的基类,封装了基本的电气设备属性和行为。它包含通用的电压属性和方法,供子类继承和实现。

主要特性:

String s: 表示设备的类型标识符(例如 开关、灯等)。

String id: 每个设备的唯一标识符。

double shuV: 设备的电压,默认为 220 伏特。

主要方法:

display(): 用于在子类中实现设备状态的显示,可以在子类中被覆盖。

reshuV(double shuop): 更新设备的电压,具体逻辑可在子类中实现。

 

(2)ControlDevice类:

该类继承自 Electric,表示能够被调节的控制设备,如开关和分档设备。它包含与调节速度或连续值相关的属性和方法。

主要特性:

int speed: 表示设备的速度,初始值为 0。

double lin: 连续值,初始值为 0。

String ofopen: 表示开关的状态,初始值为 "turned on"。

主要方法:

regulate(String vs): 用于调节相关的速度或连续值。具体实现由子类提供。

 

(3)ControlledDevice类:

该类也继承自 Electric,用于表示实际的受控设备,如灯具和风扇。它提供特定的显示逻辑和电压更新方法。

主要方法:

display(): 实现受控设备的状态显示逻辑。

reshuV(double shuop): 更新电压属性。

 

(4)Kaiguan类:

该类继承自 ControlDevice,表示一个开关设备。它通过布尔值来表示开关的状态(开启或关闭)。

主要特性:

boolean isOn: 布尔值,表示开关的状态,默认是开启。

主要方法:

display(): 根据开关状态显示当前状态(开启或关闭)。

toggle(): 切换开关状态,更新电压。

 

(5)Fendang类:

该类继承自 ControlDevice,表示一个分档设备,能够在多个速度档位之间切换。

主要特性:

MAX_SPEED: 定义最大速度(3)。

MIN_SPEED: 定义最小速度(0)。

主要方法:

display(): 显示当前的设备速度。

regulate(String vs): 根据输入调节速度,增加或减小速度档位。

reshuV(double shuop): 根据当前速度计算新的电压。

 

(6)Lianxu类

该类继承自 ControlDevice,表示一个可以连续调节的设备,能够接收并处理浮点数输入。

主要方法:

display(): 显示当前的连续值。

regulate(String vs): 接收并更新连续值。

reshuV(double shuop): 根据连续值计算新的电压。

 

(7)Baichi类

该类继承自 ControlledDevice,代表一盏白炽灯。根据电压状态显示灯的亮度。

主要方法:

display(): 根据电压显示灯的亮度,计算灯的功率输出。

 

(8)Riguang类:

该类继承自 ControlledDevice,表示一盏日光灯。能够基于当前电压状态显示灯的工作状态。

主要方法:

display(): 根据电压状态显示灯的亮度。

 

(9)Diaoshan类:

该类继承自 ControlledDevice,表示一个吊扇。根据电压计算和显示风扇的转速。

主要方法:

display(): 根据电压计算风扇的转速并显示。

 

(10)Circuit类:

该类表示电路,包含多个电气设备。负责管理和更新电气设备的状态。

主要特性:

List<Electric> devices: 存储电路中的所有电气设备。

主要方法:

addDevice(Electric device): 向电路中添加新的电气设备。

updateVoltage(double voltage): 更新整个电路中所有设备的电压。

displayDevices(): 显示所有设备的当前状态。

 

(11)核心类图:

1b81ad6fab463137fa7bf5967cdf90f

 

2. 主程序逻辑

(1)创建输入流:

Scanner in = new Scanner(System.in);

创建一个 Scanner 对象,用于从标准输入读取用户输入。

(2)初始化变量:

String s = "", pop = "", zong = "";

int i;

ArrayList<String> connection = new ArrayList<>();

ArrayList<String> chw = new ArrayList<>();

  

初始化变量以存储输入行、处理后的数据和设备连接信息。

(3)输入循环:

while (true) { ... }

  

进入一个无限循环,用于持续接收用户输入,直到遇到 "end" 关键词。

(4)处理输入行:

处理连接信息:

if (s.startsWith("[")) {

pop = s.substring(1, s.length() - 1);

...

connection.add(zong);}

  

如果输入行以 [ 开头,截取其中的数据并去掉前后的括号,存储到 connection 列表中。

处理控制指令:

else if (s.startsWith("#")) {

Pattern pattern = Pattern.compile("#(.*)\\s*");

Matcher matcher = pattern.matcher(s);

...

chw.add(pop);}

  

如果输入行以 # 开头,使用正则表达式提取指令内容,存储到 chw 列表中。

(5)初始化电路和设备管理:

double shuchuv = 220;

int kllk = 0, mmm = 0;

String z1 = "";

Map<String, Electric> map1 = new HashMap<>();

Circuit circuit = new Circuit();

  

初始化电压、控制变量、存储设备的映射以及创建电路对象。

(6)处理设备连接:

循环遍历 connection 列表,根据提供的设备信息进行设备的创建和添加到电路中。

根据每个连接信息的首个字母(如 "VCC", "GND", "L"等)处理不同的设备类型,调用 complain 方法创建相应的设备实例并将其添加到 circuit 中。

(7)处理控制指令:

在遍历 chw 列表时,根据指令类型处理对应的设备(如开关、速度调节等)。

对于 K(开关),切换开关状态并更新电压。

对于 F(分档调节器),根据 "+"/"-" 控制速度。

对于 L(连续调节器),将新值设置为设备的连续值并更新电压。

(8)计算最终电压:

根据不同设备状态和命令计算最终的电压值 rui,更新所有设备的电压。

(9)更新电路设备的电压:

circuit.updateVoltage(rui);

调用 Circuit 类的方法,更新电路中所有设备的电压。

(10)显示设备状态:

circuit.displayDevices();

最后,调用方法显示所有电气设备的当前状态,包括各个设备的亮度、速度等。

3. 迭代带来的改变:

 家居强电电路模拟程序-2在 家居强电电路模拟程序-1的基础上,新增内容和改进点如下:

(1)电路结构

本次迭代引入了并联电路的概念,允许电路中包含多个并联支路。

并联电路由多条串联电路组成,每条串联电路的输入和输出端分别短接在一起,形成一个整体的并联电路。

(2)输入信息

新增了并联电路的输入格式,格式为 #M+电路编号+":"+”[”+串联电路信息+" "+....+" "+串联电路信息+”]”。

例如:#M1:[T1 T2 T3] 表示M1是一个由T1、T2、T3三条串联电路并联组成的电路。

(3)计算方式

本次迭代开始考虑电路中的电流和电阻,不再仅限于简单的电压计算。

需要根据欧姆定律和基尔霍夫定律进行更复杂的电路分析,确保计算结果的准确性。

(4)电路元件

本次迭代没有新增具体的设备类型,但为未来增加新设备类型打下了基础。

未来迭代可能会增加更多种类的控制设备和受控设备,如继电器、变压器等。

(5)输出信息

输出信息更加详细,不仅包括设备的状态或参数,还可能包括电路中的电流、电压等中间计算结果。

例如:@K1:turned on、@B1:190、@L1:0.60。

(6)默认规则

计算过程中,所有可能出现小数的数值用double类型保存并计算,最终结果用截尾规则去掉小数部分,只保留整数部分。

连接信息必须按电路从电源到接地的顺序依次输入,确保电路的正确性。

 

4. 代码评价:

IMG_256

根据提供的 Kiviat 图形和区块直方图,我们可以得出以下几点评价:

(1)代码复杂度

Kiviat 图形中的 "Avg Complexity" 和 "Max Complexity" 分别对应平均复杂度和最大复杂度。从图形上看,这两个指标都落在绿色区域,这意味着代码的复杂度在可接受的范围内,没有出现过高复杂度的情况。

(2)注释覆盖率

Kiviat 图形中的 "% Comments" 是指注释所占的百分比。从图形上看,这个指标也落在绿色区域,说明代码有充足的注释,有助于其他开发者更好地理解和维护代码。

(3)方法和类的分布

Kiviat 图形中的 "Methods/Class" 是指每个类中的方法数量。从图形上看,这个指标接近绿色区域,表明类设计得当,方法数量适中,不会因方法过多而导致类变得臃肿难懂。

(4)代码深度

Kiviat 图形中的 "Avg Depth" 和 "Max Depth" 分别对应平均深度和最大深度。从图形上看,这两个指标接近黄色区域,提示存在一些较深层次的嵌套结构,尽管不是严重问题,但仍需注意优化,以提高代码的可读性和可维护性。

(5)区块分布

区块直方图展示了代码块的数量与其深度的关系。从图中可以看出,大多数代码块的深度在5级左右,这是一个合理的深度范围。然而,也有少量代码块的深度达到了9级以上,这可能是潜在的风险点,需要进一步检查和优化。

总体来看,代码的质量较高,特别是在复杂度管理和注释覆盖方面表现良好。不过,在代码深度方面仍有改进的空间,特别是对于那些深度较大的代码块,应该仔细审查并尽可能地简化,以提高整个项目的可读性和可维护性。

 

4. 踩坑心得

在多个类中,电压和电阻的计算逻辑分散在各个地方,这种做法会导致以下问题:

重复代码:多个地方实现相同或类似的计算逻辑,导致代码冗余,增加了代码的维护难度和出错的可能性。

维护困难:如果需要对计算逻辑进行修改,必须在多个类中寻找并更新相关代码,增加了出错风险。

可读性差:由于相关逻辑分散在不同的类中,导致代码的整体可读性降低,其他开发者在理解代码时可能需要花费更多时间。

具体解决措施:

(1)引入统一的电压和电阻计算类

创建一个专门的工具类,例如ElectricityUtils,该类集中处理所有与电压和电阻有关的计算。这种做法有助于以下几点:

集中控制:所有计算逻辑集中在一个地方,避免每个类都有自己的实现,逻辑一致且易于管理。

重用性:任何需要进行电压或电阻计算的地方,只需调用该工具类的方法,减少复制代码的需要。

易于测试:可以为该工具类编写单元测试,确保计算逻辑的正确性,测试覆盖率更高,保证了代码的可靠性。

class ElectricityUtils {

// 计算电压

public static double calculateVoltage(double current, double resistance) {

// 确保电阻大于零,以避免计算错误

if (resistance <= 0) {

throw new IllegalArgumentException("Resistance must be greater than zero.");

}

return current * resistance; // 使用欧姆定律计算电压

}

// 计算电阻

public static double calculateResistance(double voltage, double current) {

// 确保电流大于零,以避免计算错误

if (current <= 0) {

throw new IllegalArgumentException("Current must be greater than zero.");

}

return voltage / current; // 返回通过公式计算的电阻

}}

  

(2)修改现有类以使用统一的计算类

将原有设备类中的电压和电阻计算逻辑替换为对ElectricityUtils类的调用。例如,在控制设备(如开关、风扇等)的类中,更新电压和电阻的逻辑,统一使用工具类的静态方法来进行这些计算。

class ControlDevice extends Electric {

// 更新电压的函数

public void reshuV(double current) {

this.shuV = ElectricityUtils.calculateVoltage(current, this.resistance); // 使用工具类计算电压

}}

  

(3)编写单元测试

在确认已集成的ElectricityUtils类中,编写针对所有方法的单元测试,以确保每个计算都具有正确的输出,并符合预期。这不仅提高了代码的可靠性,也为日后的代码更改提供了保障。

@Test

public void testCalculateVoltage() {

assertEquals(220, ElectricityUtils.calculateVoltage(1, 220), 0.01);

assertThrows(IllegalArgumentException.class, () -> {

ElectricityUtils.calculateVoltage(1, 0); // 输入无效时应抛出异常

});}

@Test

public void testCalculateResistance() {

assertEquals(110, ElectricityUtils.calculateResistance(220, 2), 0.01);

assertThrows(IllegalArgumentException.class, () -> {

ElectricityUtils.calculateResistance(220, 0); // 输入无效时应抛出异常

});}

5. 改进建议

(1)优化输入处理:

可以考虑简化输入处理流程,使用更直接的方法来处理输入格式,即使避免使用正则表达式,改用简单的字符串拆分,可以提高代码效率和可读性。

(2)提取公共逻辑:

将电压和电阻的相关计算逻辑提取为单独的方法,中心化处理,避免在各个类中重复实现相同的逻辑。

(3)模块化设计:

目前所有的逻辑都在main方法中处理,可以考虑将其拆分成更小的方法或者独立的类来提高可维护性和可测试性。

 

三、 实验总结

通过本次实验,我学到了以下几个方面:

1. 电路结构的扩展:

引入了并联电路的概念,支持更复杂的电路结构,提高了系统的灵活性和实用性。

2. 输入信息的多样化:

新增了并联电路的输入格式,使系统能够处理更复杂的电路连接信息。

3. 电路分析的深入:

考虑了电路中的电流和电阻,使用欧姆定律和基尔霍夫定律进行更复杂的电路分析,确保计算结果的准确性。

4. 代码结构的优化:

通过引入统一的电压和电阻计算类 `ElectricityUtils`,集中处理计算逻辑,减少了代码冗余,提高了代码的可维护性和可读性。

5. 单元测试的重要性:

编写了针对 `ElectricityUtils` 类的单元测试,确保了计算方法的正确性,提高了代码的可靠性。

6. 代码文档和注释:

为关键方法添加了详细的文档和注释,帮助其他开发者快速理解代码的功能和参数含义。

7. 模块化设计:

将复杂的逻辑拆分为更小的方法或独立的类,提高了代码的可维护性和可测试性。

 

这些经验不仅帮助我解决了当前的问题,还为未来的开发工作打下了坚实的基础。

posted @ 2024-11-23 21:52  钱玥  阅读(95)  评论(0)    收藏  举报