前言
经过三周的Java开发课程,我们逐渐从基础的逻辑实现过渡到更复杂的系统设计,这不仅强化了我们对Java语言基础的理解,还深入实践了面向对象设计、异常处理、多线程和复杂数据结构等核心知识点。以下是对这三次作业的总结和反思。
第一次作业
题目开始引入复杂逻辑,包括试卷与题目关系的建立、标准答案判定、多种题型支持,难点是判定逻辑需要对不同题型(选择题、填空题)的答案进行分类处理,知识点包括各模块之间的逻辑衔接,例如题目被删除后如何影响答题结果、总分提示与答案判定的关系,确保多张试卷、多学生的情况下,结果按规则正确输出。
第二次作业
本次是一个关于智能家居的新系列,系统需要处理多个设备,并且需要以串联方式进行连接,需要动态输入和状态计算,知识点包括通过电路元件的输入,进行一系列操作,去输出相应状态,难点就是怎样通过键盘输入去完成各个元件的串联。
第三次作业
这次作业在上次基础上,多迭代了一种电路元件,并且新增了一个并联电路,输入方式也有所变化,要多考虑一些分压变化去输出电路元件状态,难点更多的是一些特殊情况,例如短路,断路等等。
设计与分析
题目集4总共设计了七个类,包括问题,单选,多选,填空,试卷,学生,答卷
1.Question 类
该类是一个抽象类,所有题型(如单选、多选、填空)都继承自它。
属性:
number:题号。
content:题目内容。
方法:
scoreAnswer(String answer, int questionScore):抽象方法,负责根据学生提供的答案与正确答案对比,计算得分。具体实现由子类定义。
formatResult(String answer, boolean isCorrect):格式化输出的结果。这个方法可以被子类重写以定制不同类型题目的输出格式。
2. SingleChoiceQuestion类
继承自 Question,用于表示单选题。
属性:
correctAnswer:正确答案。
方法:
scoreAnswer:根据学生提供的答案与正确答案比较,完全一致则得满分,否则得0分。
formatResult:返回题目的格式化结果,包含答案是否正确。
3.MultipleChoiceQuestion类
继承自 Question,用于表示多选题。
属性:
correctAnswers:一个 Set,存储正确答案的集合。
方法:
scoreAnswer:根据学生的答案和正确答案集合的比较,判断答案是否完全正确、部分正确或完全错误:
完全正确:答案与正确答案完全匹配。
部分正确:提供的答案是正确答案的子集。
错误:存在错误答案。
formatResult:格式化结果输出,区分完全正确、部分正确和错误的情况。
4.FillInTheBlankQuestion类
继承自 Question,用于表示填空题。
属性:
correctAnswers:一个 Set,存储正确答案的集合。多个答案用 "或" 连接。
方法:
scoreAnswer:根据学生提供的答案与正确答案集合比较,判断答案是否完全正确、部分正确或完全错误:
完全正确:提供的答案与正确答案完全一致。
部分正确:提供的答案是正确答案的子集。
formatResult:格式化结果输出,处理正确和部分正确的情况。
5.TestPaper类
用于表示试卷,包含试卷编号、题目和总分等信息。
属性:
testNumber:试卷编号。
questions:一个 Map,将题号映射到对应的得分。
totalScore:试卷的总分。
方法:
addQuestion(int questionNumber, int score):向试卷中添加题目,指定每道题目的得分。
6.Student类
用于表示学生,包含学生的学号和姓名。
属性:
studentID:学生学号。
name:学生姓名。
7.AnswerSheet类
用于表示学生的答卷,包含试卷ID、学生ID以及各题目的答案。
属性:
testPaperID:试卷编号。
studentID:学生学号。
answers:一个 Map,存储学生的答案,题号作为键,答案作为值。
方法:
addAnswer(int questionIndex, String answer):添加学生的答题信息
类图如下:
SourceMonitor报表如下:
代码行数为308行,语句数为169条分支语句百分比为21.9%(指的是条件分支语句的比例,例如 if/else 语句)方法调用语句数为96条(指的是代码中方法调用的次数),注释行百分比为6.5%,类和接口数:5个(有部分类在主类里面),每个类的方法数为2.80(每个类中方法的平均数量),每个方法的语句平均数为11.50最复杂方法的行号:83,最复杂方法的名称为:FillInTheBlankQuestion.scoreA,该图提供了多个指标的可视化表示,注释百分比:注释比例较低,仅为6.5%,说明代码中的注释较少。平均复杂度:复杂度适中,表明代码结构不至于过于复杂。每个类的方法数:每个类平均包含2.8个方法,表示类的结构比较简单。平均深度:方法的平均嵌套深度适中,表明代码中的方法嵌套不深。最大深度:代码中出现的最大嵌套深度,具体数值无法从图中获取。最大复杂度:图中显示的最大复杂度,可能表示最复杂的代码块或方法,该直方图显示了不同嵌套深度下的语句数量。大部分语句位于较浅的深度(深度为0到3之间),只有少量语句处于较深的嵌套层次。
题目集5设计8类,包括设备,开关,风扇速度控制器,连续速度控制器,白炽灯,日光灯,风扇,电路
1.Device类
属性:
String id:设备的标识符。
double inputVoltage:输入电压。
double outputVoltage:输出电压。
方法:
abstract void process():每个设备都有自己处理并输出其状态的方法。
abstract void updateVoltage(double inputVoltage):每个设备根据输入电压来更新其输出电压
2.Switch类
属性:
boolean isClosed:表示开关是否关闭(开/关状态)。
方法:
toggle():切换开关的状态。
process():输出当前开关的状态(开/关)。
updateVoltage(double inputVoltage):根据开关的状态更新输出电压。如果开关是闭合的,输出与输入电压相同;如果开关是打开的,输出电压为0。
3.FanSpeedController类
属性:
int level:表示风扇的速度等级,范围从 0 到 3。
方法:
increaseLevel():增加风扇速度等级,最大为 3。
decreaseLevel():降低风扇速度等级,最小为 0。
process():输出当前的风扇速度等级。
updateVoltage(double inputVoltage):根据输入电压和当前的风扇速度等级更新输出电压
4.ContinuousSpeedController类
属性:
double level:表示连续速度等级,范围从 0.00 到 1.00。
方法:
setLevel(double level):设置速度等级,确保在 0.00 到 1.00 之间。
process():输出当前的速度等级,表示为百分比。
updateVoltage(double inputVoltage):根据输入电压和当前的速度等级更新输出电压
5.IncandescentLight类
属性:
double brightness:表示灯光的亮度。
方法:
process():输出灯光的亮度值。
updateVoltage(double inputVoltage):根据输入电压调整亮度。如果电压小于 9V,亮度为 0;如果电压大于 220V,亮度为最大 200;否则,亮度根据电压线性变化。
6.FluorescentLight类
属性:
double brightness:表示灯光的亮度。
方法:
process():输出灯光的亮度值。
updateVoltage(double inputVoltage):如果电压大于 0V,亮度为 180;如果电压为 0V,亮度为 0。
7.Fan类
属性:
double speed:表示风扇的速度。
方法:
process():输出风扇的速度。
updateVoltage(double inputVoltage):根据输入电压调整风扇的速度。
8.Circuit类
属性:
Map<String, Device> devices:通过设备 ID 存储所有设备。
List<String[]> connections:存储设备之间的连接关系。
方法:
addDevice(Device device):将设备添加到电路中。
addConnection(String[] connection):添加设备之间的连接。
processCommands(List
simulate():模拟电压在电路中的流动,并根据每个设备的连接更新电压。
printDeviceStates():输出电路中所有设备的状态。
类图如下:
SourceMonitor报表如下:
通过图片而知,代码总行数为 322 行,代码中共有 225 条可执行语句,分支语句百分比表明24.9% 的语句为分支语句(如条件判断),方法调用语句数表明代码中有 64 条方法调用语句,含注释的行百分比表示只有 0.9% 的行包含注释,表明代码中几乎没有文档说明,类和接口数表明代码中有 9 个类和接口,每个类的方法数表明每个类平均有 3.33 个方法,每个方法平均包含 5.33 条语句,最复杂的方法从第 235 行开始,方法名为 Circuit.printDeviceState,代码中的注释比例仅为 0.9%,每个类的方法数相对较少,平均只有 3.33 个方法,这表明类比较大,每个方法平均包含 5.33 条语句,复杂度相对适中,但最复杂的方法位于第 235 行,这可能是代码中最难理解的部分。直方图表明,大多数代码块的语句数较少,复杂度较低,但也有一些代码块深度较大或复杂度较高。
题目集6相比上次新增了落地扇类,并对电路类进行相应的修改,其余没有变化。
1.FloorFan类
属性:
speed:风扇的速度
方法:
process():打印风扇速度
updateVoltage(double inputVoltage):根据输入电压更新速度
2.Circuit类
属性:
devices:设备的映射
seriesCircuits:串联电路的映射
parallelCircuits:并联电路的映射
方法:
addDevice(Device device):添加设备到电路
addSeriesCircuit(String id, List<String[]> connections):添加串联电路
addParallelCircuit(String id, List
processCommands(List
simulate():模拟电路
simulateSeriesCircuit(String circuitId):模拟串联电路
simulateParallelCircuit(String circuitId):模拟并联电路
printDeviceStates():打印设备状态
类图如下:
SourceMonitor报表如下:
总共有437行代码,总共有294条可执行语句,26.9%的语句是条件分支语句(例如 if、switch),共有85条方法调用语句,5%的代码行包含注释,文件中有2个类或接口, 每个类平均有21.5个方法,每个方法平均包含6.7条语句,最复杂的方法位于第402行,最复杂的方法是 Main.createDevice(),代码注释的比例较低,仅为5%,该文件的结构相对较复杂,语句中有近27%是分支语句,且有85条方法调用语句。每个类包含大量方法(21.5个),这可能意味着类承担了过多的功能,可能需要通过重构来提高可读性或模块化。最复杂的方法为Main.createDevice() 方法被标识为最复杂的方法,且位于第402行。考虑到它可能承载了文件中的主要复杂性,可能需要重点优化或审查。
踩坑心得
1.最后一次题目类输出时,,如果是多选题的话,输出时只输出一个答案,如下图所示:
代码如下:
点击查看代码
public String formatResult(String answer, boolean isCorrect) {
Set<String> providedAnswers = new HashSet<>(Arrays.asList(answer.trim().split("或")));
if (isCorrect) {
return super.formatResult(answer, true);
} else if (correctAnswers.containsAll(providedAnswers) && !providedAnswers.isEmpty()) {
return content + "~" + answer + "~partially correct";
} else {
return super.formatResult(answer, false);
}
}
}
逻辑上分为三种情况,对正确,部分正确,错进行处理,但是处理部分正确的答案的时候这里应该是只对第一个输入的答案进行了输出,输出的话就是上图情况,一时可能没想到怎么去解决。
2.第二次作业的样例全可以通过,但是仍在一些测试点上无法通过。
点击查看代码
class IncandescentLight extends Device {
double brightness;
public IncandescentLight(String id) {
super(id);
this.brightness = 0.0;
}
@Override
public void process() {
System.out.println("@" + id + ":" + (int) brightness);
}
@Override
public void updateVoltage(double inputVoltage) {
this.inputVoltage = inputVoltage;
if (inputVoltage <= 9) {
this.brightness = 0.0;
} else if (inputVoltage > 220) {
this.brightness = 200.0;
} else {
this.brightness = (inputVoltage - 10) * (150.0 / 210) + 50;
}
}
}
class FluorescentLight extends Device {
double brightness;
public FluorescentLight(String id) {
super(id);
this.brightness = 0.0;
}
@Override
public void process() {
System.out.println("@" + id + ":" + (int) brightness);
}
@Override
public void updateVoltage(double inputVoltage) {
this.inputVoltage = inputVoltage;
this.brightness = inputVoltage > 0 ? 180.0 : 0.0;
}
}
class Fan extends Device {
double speed;
public Fan(String id) {
super(id);
this.speed = 0.0;
}
@Override
public void process() {
System.out.println("@" + id + ":" + (int) speed);
}
@Override
public void updateVoltage(double inputVoltage) {
this.inputVoltage = inputVoltage;
if (inputVoltage < 80) {
this.speed = 0.0;
} else if (inputVoltage >= 80 && inputVoltage <= 150) {
this.speed = (inputVoltage - 80) * (280.0 / 70) + 80;
} else {
this.speed = 360.0;
}
}
}
感觉逻辑上没有问题,IncandescentLight 类更新输入电压。根据输入电压计算亮度:低于或等于 9V,亮度为 0。高于 220V,亮度为 200。介于 10V 和 220V 之间:根据公式计算亮度,FluorescentLight类更新输入电压。如果输入电压大于 0,亮度为 180;如果输入电压为 0,亮度为 0,Fan类更新输入电压。如果输入电压小于 80V,转速为 0;如果在 80V 到 150V 之间,根据公式计算转速;如果超过 150V,转速为 360,测试样例中有关这三个的也进行了测试,也符合输出,但是仍不知道问题所在,比较令人苦恼
3.在新加入新电路元件和并联电路,分压出现了问题
对于获取串联电路连接信息
点击查看代码
List<String[]> connections = seriesCircuits.get(circuitId);
if (connections == null) return;
点击查看代码
double totalResistance = 0.0;
for (String[] connection : connections) {
String inputId = connection[0].split("-")[0];
Device inputDevice = devices.get(inputId);
if (inputDevice != null) {
totalResistance += inputDevice.resistance;
}
}
点击查看代码
for (String[] connection : connections) {
String inputId = connection[0].split("-")[0];
String outputId = connection[1].split("-")[0];
Device inputDevice = devices.get(inputId);
Device outputDevice = devices.get(outputId);
if (inputDevice != null && outputDevice != null) {
double voltageDrop = voltage * (inputDevice.resistance / totalResistance);
inputDevice.updateVoltage(voltageDrop);
voltage -= voltageDrop;
outputDevice.updateVoltage(voltage);
}
}
获取并联电路的信息
点击查看代码
List<String> seriesIds = parallelCircuits.get(circuitId);
if (seriesIds == null) return;
点击查看代码
for (String seriesId : seriesIds) {
List<String[]> connections = seriesCircuits.get(seriesId);
if (connections == null) continue;
点击查看代码
double totalResistance = 0.0;
for (String[] connection : connections) {
String inputId = connection[0].split("-")[0];
Device inputDevice = devices.get(inputId);
if (inputDevice != null) {
totalResistance += inputDevice.resistance;
}
}
改进建议
要优化命名、增加注释,提升代码的可读性,有时候类分的也比较固定,容易限制思维,而对于这几次题,给的测试样例也逐渐减少,要多去自己想一些特殊情况,例如短路等等,感觉太局限于测试样例,仿佛是为了答案去解题,从而失去自己对题的一些正常看待,应该通过过程推结果,思维多发散一下。
总结
最后一次试卷类全新的大作业进行迭代,加入了多选题之类的迭代,通过分离不同的功能模块来进行操作,而全新的两次迭代,学会了单一职责原则和开闭原则,对类进行封装和继承。java学习任重而道远,需要不断在一次次修改,迭代中学会更多,以后应该为大作业留一些更多时间,先大概有个思路,最后不断去塑造出代码。
标签:总结,语句,题目,String,double,电压,inputVoltage,方法 From: https://www.cnblogs.com/cxk25/p/18565194