第二次博客作业
一、前言
第一次作业
第一次作业有三道题,前两道道为入门的小题,最后一题在之前的答题判断程序上进行了第四次迭代,在题目方面添加了多选题以及填空题,试卷方面添加了多张试卷多个同学答题的输入情况,判题方面添加了部分正确这一得分级别,整体难度较大。
第二次作业
第二次作业有三道题,后两道为入门及简单的小题,第一题为难度较大的家居电路模拟程序。主要考察对类的属性和方法的设计与使用,以及在规定格式下对程序用户的输入进行解析从而获取电器设备以及操作等信息,并给与相应的输出。
第三次作业
第三次作业只有一道题,即在前一次家居电路模拟程序的基础上进行迭代。添加了并联电路类,改变了输入格式,难度较大。
二、设计与分析
第一次作业
SourceMontor报表
3.7% 的代码行包含注释,2 个类中没有特别指出类和接口的注释比例。
平均每个类有 4 个方法,每个方法平均有 7.62 条语句,方法的平均复杂度目标没有特别指出。最大块深度为 2(指代码块嵌套的层数);平均块深度为 1.08;平均复杂度为 1.00(指方法中不同路径的数量,较高的复杂度可能意味着方法难以测试和维护)表明代码的复杂度控制得相对较好。
最复杂的方法是 Question.Question(),位于第 11 行。这可能表明 Question.Question() 方法过于复杂,需要进一步的分解或重构或者需要拆分;其复杂度为 1,包含 11 条语句,最大深度为 2,调用了 0 次方法调用。这表明 Question.Question() 方法可能需要进一步的优化。
类图
Question 类:
包含四个成员变量 id、content、answer 和 isDeleted,表示一个题目的基本信息、答案和删除状态。
包含一个构造方法,用于初始化题目的基本信息。
包含一个静态工厂方法 parseFromInput,用于从输入字符串解析并创建 Question 对象。
包含一个抽象方法 grade,用于根据学生答案和满分计算得分,由子类实现具体的判分逻辑。
Paper 类:
包含三个成员变量 id、questionIdToScore 和 sequenceToQuestionId,表示一个试卷的基本信息、题目分值映射和题目顺序。
包含一个构造方法,用于初始化试卷的基本信息。
包含一个静态工厂方法 parseFromInput,用于从输入字符串解析并创建 Paper 对象。
包含一个方法 getTotalScore,用于计算试卷的总分。
Student 类:
包含两个成员变量 id 和 name,表示学生的基本信息。
包含一个构造方法,用于初始化学生的基本信息。
包含一个静态工厂方法 parseFromInput,用于从输入字符串解析并创建 Student 对象列表。
AnswerSheet 类:
使用了 Paper 和 Student 对象来表示答卷信息。包含三个成员变量 paperId、studentId 和 sequenceToAnswer,表示一个答卷的基本信息、学生答案和题目顺序。
包含一个构造方法,用于初始化答卷的基本信息。
包含一个静态工厂方法 parseFromInput,用于从输入字符串解析并创建 AnswerSheet 对象。
Main 类:
包含 main 方法,是程序的入口点,包含数据存储结构和业务逻辑。
使用 HashMap、LinkedHashMap 和 ArrayList 来存储 Question、Paper、Student 和 AnswerSheet 对象。
处理输入数据,包括题目、试卷、学生和答卷信息,并进行相应的业务逻辑处理,如判分、输出错误信息和警告信息。
第二次作业
SourceMontor报表
12.9% 的代码行包含注释,没有特别指出类和接口的注释比例。
平均每个类有 3.67 个方法,每个方法平均有 6.03 条语句,方法的平均复杂度目标没有特别指出。最大块深度为 7(指代码块嵌套的层数);平均块深度为 2.85;平均复杂度为 1.82(指方法中不同路径的数量,较高的复杂度可能意味着方法难以测试和维护)表明代码的复杂度控制得相对较好。
最复杂的方法是 Circuit.calculateVoltages(),位于第 210 行。这可能表明 Circuit.calculateVoltages() 方法过于复杂,需要进一步的分解或重构或者需要拆分;其复杂度为 9,包含 16 条语句,最大深度为 7,调用了 14 次方法调用。这表明 Circuit.calculateVoltages() 方法可能需要进一步的优化。
类图
ControlDevice 类:
包含两个成员变量 id、inputVoltage 和 outputVoltage,表示控制设备的标识符、输入电压和输出电压。
包含一个构造方法,用于初始化控制设备的标识符。
包含一个抽象方法 updateOutputVoltage,用于更新设备的输出电压。
包含一个抽象方法 getStatus,用于获取设备的当前状态。
Switch 类:
包含一个成员变量 isClosed,表示开关的当前状态。
包含一个构造方法,用于初始化开关的标识符。
包含一个方法 toggle,用于切换开关的状态。
继承并实现 updateOutputVoltage 方法,根据开关状态更新输出电压。
继承并实现 getStatus 方法,返回开关的当前状态。
StepSpeedController 类:
包含一个成员变量 level,表示分档调速器的当前档位。
包含一个构造方法,用于初始化调速器的标识符。
包含两个方法 incrementLevel 和 decrementLevel,用于调整档位。
继承并实现 updateOutputVoltage 方法,根据档位更新输出电压。
继承并实现 getStatus 方法,返回调速器的当前档位。
ContinuousSpeedController 类:
包含一个成员变量 position,表示连续调速器的当前位置参数。
包含一个构造方法,用于初始化调速器的标识符。
包含一个方法 setPosition,用于设置调速器的位置参数。
继承并实现 updateOutputVoltage 方法,根据位置参数更新输出电压。
继承并实现 getStatus 方法,返回调速器的当前位置参数。
ControlledDevice 类:
包含两个成员变量 id、inputVoltage 和 outputVoltage,表示受控设备的标识符、输入电压和输出电压。
包含一个构造方法,用于初始化受控设备的标识符。
包含一个抽象方法 updateOutputVoltage,用于更新设备的输出电压。
包含一个抽象方法 getStatus,用于获取设备的当前状态。
IncandescentLamp 类:
包含一个构造方法,用于初始化白炽灯的标识符。
包含一个方法 calculateBrightness,用于计算灯具的亮度。
继承并实现 updateOutputVoltage 方法,白炽灯不需要更新输出电压。
继承并实现 getStatus 方法,返回灯具的亮度。
FluorescentLamp 类:
包含一个构造方法,用于初始化日光灯的标识符。
包含一个方法 calculateBrightness,用于计算灯具的亮度。
继承并实现 updateOutputVoltage 方法,日光灯不需要更新输出电压。
继承并实现 getStatus 方法,返回灯具的亮度。
CeilingFan 类:
包含一个构造方法,用于初始化吊扇的标识符。
包含一个方法 calculateSpeed,用于计算风扇的转速。
继承并实现 updateOutputVoltage 方法,吊扇不需要更新输出电压。
继承并实现 getStatus 方法,返回风扇的转速。
Circuit 类:
包含三个成员变量 controlDevices、controlledDevices 和 connections,分别存储控制设备、受控设备和设备间的连接关系。
包含三个方法 addControlDevice、addControlledDevice 和 connectDevices,用于添加控制设备、受控设备和连接设备。
包含一个方法 processCommand,用于处理控制命令。
包含一个方法 calculateVoltages,用于计算电路中所有设备的电压。
包含一个方法 outputStates,用于输出所有设备的状态。
Main 类:
包含 main 方法,是程序的入口点,负责读取输入并模拟电路系统的操作。
在 main 方法中,使用 Circuit 对象来管理电路中的所有设备和连接。
处理输入数据,创建相应的设备对象,连接设备,并处理控制命令。
最后,计算电路中的电压并输出所有设备的状态。
第三次作业
SourceMontor报表
13.1% 的代码行包含注释,没有特别指出类和接口的注释比例。
平均每个类有 4.78 个方法,每个方法平均有 5.42 条语句,方法的平均复杂度目标没有特别指出。最大块深度为 7(指代码块嵌套的层数);平均块深度为 2.23;平均复杂度为 1.57(指方法中不同路径的数量,较高的复杂度可能意味着方法难以测试和维护)表明代码的复杂度控制得相对较好。
最复杂的方法是 Circuit.processCommand(),位于第 282 行。这可能表明 Circuit.processCommand() 方法过于复杂,需要进一步的分解或重构或者需要拆分;其复杂度为 9,包含 23 条语句,最大深度为 4,调用了 19 次方法调用。这表明 Circuit.processCommand() 方法可能需要进一步的优化。
类图
ControlDevice 类:
包含三个成员变量:设备标识符 id,输入电压 inputVoltage,输出电压 outputVoltage。
包含一个构造方法,用于初始化设备标识符。
包含两个抽象方法:updateOutputVoltage 用于更新设备的输出电压,getStatus 用于获取设备的当前状态。
点击查看代码
// 控制设备的父类
abstract class ControlDevice {
String id; // 设备标识符
double inputVoltage = 0; // 输入电压
double outputVoltage = 0; // 输出电压
ControlDevice(String id) {
this.id = id;
}
// 更新输出电压的抽象方法,具体实现由子类完成
abstract void updateOutputVoltage();
// 获取设备状态的抽象方法,具体实现由子类完成
abstract String getStatus();
}
Switch 类:
包含一个成员变量 isClosed,表示开关的开闭状态。
包含一个构造方法,用于初始化开关的标识符。
包含一个方法 toggle,用于切换开关的状态。
重写 updateOutputVoltage 方法,根据开关状态更新输出电压。
重写 getStatus 方法,返回开关的当前状态。
点击查看代码
// 开关类,继承自 ControlDevice
class Switch extends ControlDevice {
boolean isClosed = false; // 初始状态为打开
Switch(String id) {
super(id);
}
// 切换开关状态
void toggle() {
isClosed = !isClosed;
}
@Override
void updateOutputVoltage() {
// 开关闭合时输出电压等于输入电压,否则为0
outputVoltage = isClosed ? inputVoltage : 0;
}
@Override
String getStatus() {
return "@" + id + ":" + (isClosed ? "closed" : "turned on");
}
}
StepSpeedController 类:
包含一个成员变量 level,表示分档调速器的档位。
包含一个构造方法,用于初始化调速器的标识符。
包含两个方法 incrementLevel 和 decrementLevel,用于调整档位。
重写 updateOutputVoltage 方法,根据档位更新输出电压。
重写 getStatus 方法,返回调速器的当前档位。
点击查看代码
// 分档调速器类,继承自 ControlDevice
class StepSpeedController extends ControlDevice {
int level = 0; // 初始档位为0
StepSpeedController(String id) {
super(id);
}
// 增加档位
void incrementLevel() {
if (level < 3) level++;
}
// 减少档位
void decrementLevel() {
if (level > 0) level--;
}
@Override
void updateOutputVoltage() {
// 根据档位调整输出电压
double[] factors = {0, 0.3, 0.6, 0.9};
outputVoltage = inputVoltage * factors[level];
}
@Override
String getStatus() {
return "@" + id + ":" + level;
}
}
ContinuousSpeedController 类:
包含一个成员变量 position,表示连续调速器的位置参数。
包含一个构造方法,用于初始化调速器的标识符。
包含一个方法 setPosition,用于设置调速器的位置参数。
重写 updateOutputVoltage 方法,根据位置参数更新输出电压。
重写 getStatus 方法,返回调速器的当前位置参数。
点击查看代码
// 连续调速器类,继承自 ControlDevice
class ContinuousSpeedController extends ControlDevice {
double position = 0.00; // 初始档位参数为0.00
ContinuousSpeedController(String id) {
super(id);
}
// 设置档位参数
void setPosition(double position) {
this.position = position;
}
@Override
void updateOutputVoltage() {
outputVoltage = inputVoltage * position;
}
@Override
String getStatus() {
return String.format("@%s:%.2f", id, position);
}
}
ControlledDevice 类:
包含三个成员变量:设备标识符 id,输入电压 inputVoltage,输出电压 outputVoltage。
包含一个构造方法,用于初始化设备标识符。
包含两个抽象方法:updateOutputVoltage 用于更新设备的输出电压,getStatus 用于获取设备的当前状态。
点击查看代码
// 受控设备父类
abstract class ControlledDevice {
String id; // 设备标识符
double inputVoltage = 0; // 输入电压
double outputVoltage = 0; // 输出电压
ControlledDevice(String id) {
this.id = id;
}
// 更新输出电压的方法,具体实现由子类完成
abstract void updateOutputVoltage();
// 获取设备状态的方法,具体实现由子类完成
abstract String getStatus();
}
IncandescentLamp 类:
包含一个构造方法,用于初始化白炽灯的标识符。
包含一个方法 calculateBrightness,用于计算灯具的亮度。
重写 updateOutputVoltage 方法,白炽灯不需要更新输出电压。
重写 getStatus 方法,返回灯具的亮度。
点击查看代码
// 白炽灯类,继承自 ControlledDevice
class IncandescentLamp extends ControlledDevice {
IncandescentLamp(String id) {
super(id);
}
// 计算亮度
int calculateBrightness(double voltageDifference) {
//略
}
@Override
void updateOutputVoltage() {
// 灯具不需要更新输出电压,依赖电压差
}
@Override
String getStatus() {
double voltageDifference = Math.abs(inputVoltage - outputVoltage);
return "@" + id + ":" + calculateBrightness(voltageDifference);
}
}
FluorescentLamp 类:
包含一个构造方法,用于初始化日光灯的标识符。
包含一个方法 calculateBrightness,用于计算灯具的亮度。
重写 updateOutputVoltage 方法,日光灯不需要更新输出电压。
重写 getStatus 方法,返回灯具的亮度。
点击查看代码
// 日光灯类,继承自 ControlledDevice
class FluorescentLamp extends ControlledDevice {
FluorescentLamp(String id) {
super(id);
}
// 计算亮度
int calculateBrightness(double voltageDifference) {
//略
}
@Override
void updateOutputVoltage() {
// 灯具不需要更新输出电压,依赖电压差
}
@Override
String getStatus() {
double voltageDifference = Math.abs(inputVoltage - outputVoltage);
return "@" + id + ":" + calculateBrightness(voltageDifference);
}
}
CeilingFan 类:
包含一个构造方法,用于初始化吊扇的标识符。
包含一个方法 calculateSpeed,用于计算风扇的转速。
重写 updateOutputVoltage 方法,吊扇不需要更新输出电压。
重写 getStatus 方法,返回风扇的转速。
点击查看代码
// 吊扇类,继承自 ControlledDevice
class CeilingFan extends ControlledDevice {
CeilingFan(String id) {
super(id);
}
// 计算转速
int calculateSpeed(double voltageDifference) {
//略
}
@Override
void updateOutputVoltage() {
// 风扇不需要更新输出电压,依赖电压差
}
@Override
String getStatus() {
double voltageDifference = Math.abs(inputVoltage - outputVoltage);
return "@" + id + ":" + calculateSpeed(voltageDifference);
}
}
FloorFan 类:
包含一个构造方法,用于初始化落地扇的标识符。
包含一个方法 calculateSpeed,用于计算风扇的转速。
重写 updateOutputVoltage 方法,落地扇不需要更新输出电压。
重写 getStatus 方法,返回风扇的转速。
点击查看代码
// 落地扇类,继承自 ControlledDevice
class FloorFan extends ControlledDevice {
FloorFan(String id) {
super(id);
}
// 计算转速
int calculateSpeed(double voltageDifference) {
//略
}
@Override
void updateOutputVoltage() {
// 落地扇不需要更新输出电压,依赖电压差
}
@Override
String getStatus() {
double voltageDifference = Math.abs(inputVoltage - outputVoltage);
return "@" + id + ":" + calculateSpeed(voltageDifference);
}
}
SeriesCircuit 类:
包含两个成员变量:电路标识符 id 和连接列表 connections。
包含一个构造方法,用于初始化电路标识符。
包含一个方法 addConnection,用于添加电路连接。
点击查看代码
// 串联电路类
class SeriesCircuit {
String id;
List<String[]> connections = new ArrayList<>();
SeriesCircuit(String id) {
this.id = id;
}
void addConnection(String[] connection) {
connections.add(connection);
}
}
ParallelCircuit 类:
包含两个成员变量:电路标识符 id 和串联电路列表 seriesCircuits。
包含一个构造方法,用于初始化电路标识符。
包含一个方法 addSeriesCircuit,用于添加串联电路。
点击查看代码
// 并联电路类
class ParallelCircuit {
String id;
List<SeriesCircuit> seriesCircuits = new ArrayList<>();
ParallelCircuit(String id) {
this.id = id;
}
void addSeriesCircuit(SeriesCircuit seriesCircuit) {
seriesCircuits.add(seriesCircuit);
}
}
Circuit 类:
包含四个成员变量:控制设备映射 controlDevices,受控设备映射 controlledDevices,串联电路映射 seriesCircuits,和并联电路映射 parallelCircuits。
包含四个方法:addControlDevice 添加控制设备,addControlledDevice 添加受控设备,addSeriesCircuit 添加串联电路,addParallelCircuit 添加并联电路。
包含两个方法:processSeriesCircuit 处理串联电路,processParallelCircuit 处理并联电路。
包含一个方法 processCommand,用于处理控制命令。
包含一个方法 calculateVoltages,用于计算电路中所有设备的电压。
包含一个方法 outputStates,用于输出所有设备的状态。
点击查看代码
// 电路类,管理所有设备及连接
class Circuit {
//添加设备,电路
//处理命令
void processSeriesCircuit(String line) {
//略
}
// 计算电压
void calculateVoltages() {
//略
}
// 输出设备状态
void outputStates() {
//略
}
// 获取设备类型的排序优先级
private int getOrder(ControlDevice device) {
//略
}
}
Main 类:
包含 main 方法,是程序的入口点,负责读取输入并模拟电路系统的操作。
在 main 方法中,使用 Circuit 对象来管理电路中的所有设备和连接。
处理输入数据,创建相应的设备对象,连接设备,并处理控制命令。
最后,计算电路中的电压并输出所有设备的状态。
三、踩坑与心得
1.答案错误
答案错误往往是计算逻辑错误或者输出的格式有误。需要用到测试用例进行运行测试,通过比对预计输出以及实际输出的不同找出格式错误,比如说第一次作业有不全对的题目,要求给一半的分,但是案例测试时答案错误,通过实际输出发现是得分与预计输出不同,于是我检查判分部分的代码,才发现是判断不全对的条件写错了。
点击查看代码
if (hasAllCorrect && !hasError) {
return fullScore;
} else if (hasError || userAnswers.isEmpty()) {
return 0;
} else {
return fullScore / 2; // 部分正确,给一半分
}
2.非零返回
非零返回这一问题困扰了我许久许久,导致前两次作业不太理想,主要是不理解非零返回到底指什么,于是我查找资料,到底什么是非零返回,得到的答案是这样的:
在PTA平台上提交代码时,出现“非零返回”错误通常意味着程序在运行过程中遇到了异常情况,导致返回状态码不是零。
然并卵:)还是没说清楚什么是"返回状态码不是零",接着我继续查导致的原因,总结了以下几点:
一、逻辑错误:
描述:程序中的逻辑错误可能导致某些条件未被满足,结果抛出未捕获的异常或进入死循环。
解决方法:仔细检查代码逻辑,确保所有条件都被正确处理。使用调试器逐步执行代码,观察变量的变化,确定导致异常的具体位置和原因。
二、数组或列表越界:
描述:访问数组或列表时,索引超出范围。
解决方法:在访问数组或列表之前,检查索引是否在合法范围内。
三、非零返回值:
描述:程序中调用了System.exit()并传递了非零值。
解方法:避免直接调用System.exit(),让程序自然结束以确保返回值为0。
四、输入输出错误:
描述:如尝试读取不存在的文件或将数据写入只读的文件。
解决方法:对输入数据进行有效性检查,确保在处理之前数据的合法性。
我一条一条比对时发现,基本上都是第四种情况,说人话即输入的格式错误导致用户输入为空或不符合预期格式,然而,PTA检测时的输入是固定的,所以还是代码在解析用户输入时有的清理没有考虑到。
四、改进建议及总结
改进建议:虽然老师说测试样例少是为了让我们自己考虑到各个情况,但我还是觉得至少可以再把测试的提示写详细一点,或者说在提交时间结束之后公布出来,这样也可以在时候总结有哪些情况没有考虑到,下一次编写程序的时候就会有进步,这样达到一个学习的目的。而不是不知道就一直是懵的状态,前一次作业有没考虑进去的情况后面迭代的时候就更难了。
总结:通过本阶段三次题目集的编写以及提交,我在面向对象的思想上有了进一步的了解,学会了许多JAVA的语法函数。但对于一些相关方法还需要进一步学习及研究,希望上课时能更多的了解这方面的内容。