一.题目集四,7-3答题程序4
1.前言
知识点考查和难度:
本题在上次迭代基础上增加了题目的类型,选择填空多选,对于输入信息的筛查难度更大。判分方式对于题型也不同。
更综合考查了类的组合使用,哈希表队列的运用等。
2.设计分析
解析信息的逻辑上,我使用starswith方法判断每行输入的开头标识来区分不同类型的信息。当遇到以#N:、#Z:、#K:开头的输入行时,通过#A:、#Q:等标识符分割出题目各部分信息,解析出题目编号、题目内容和标准答案,然后创建Question对象并添加到questionMap以及对应试卷中,以此完成对不同类型题目的区分和存储。分值计算上,为了区分不同题型不同的判题方法,我在AnswerSheet类的saveAnswer方法中,根据答案核对的结果来计算每道题的得分。如果答案核对结果为true,则该题得分为试卷中对应题目的分值;如果结果为partially correct,则该题得分为对应分值的一半;若答案错误或者题目无效等情况则得分为 0 分,将每道题的得分添加到scores列表中,后续通过getJudgmentResultString方法将各题得分汇总,用于最终输出该答卷的分值情况,完成整个分值计算以及结果输出相关的流程。类图如下:
本答题判分程序,能够处理多种类型的题目(普通题目、选择题、填空题),解析不同的输入信息(题目信息、试卷信息、学生信息、答卷信息、删除题目信息),按照相应规则核对答案、计算分值,并输出符合要求的结果,整体通过多个类协同工作来完成各项功能,时序图如下:
3.踩坑心得
在本题计算不同类型的分数时,我按照样例测试,一直得不到正确的分数,后来我一点点计算答案正确的得分,发现是因为部分答案正确给半分的代码逻辑没有设计好,不光是顺序的改变或者是其他的细小的变化,我的判题程序将会给予错判的问题,并且直接扣除所有的分数。
改进后代码:
点击查看代码
public void saveAnswer(String[] answers) {
List<Integer> questionNumbers = testPaper.getQuestionNumbers();
for (int i = 0; i < questionNumbers.size(); i++) {
int questionNum = questionNumbers.get(i);
String answer = null;
for (String ans : answers) {
String[] parts = ans.split("-");
int order = Integer.parseInt(parts[0].trim());
if (order == i + 1) {
answer = parts.length > 1? parts[1].trim() : null;
break;
}
}
// 检查题目是否被删除或不存在
if (deletedQuestions.contains(questionNum) ||!paper.questions.containsKey(questionNum)) {
judgmentResults.add("non-existent question");
this.answers.add("0"); // 对于无效题目,答案设置为0
scores.add(0); // 题目无效,分数为0
} else {
String result = paper.checkAnswerWithResult(questionNum, answer);
judgmentResults.add(result);
this.answers.add(answer!= null? answer : "answer is null");
int score = 0;
if (result.equals("true")) { // 计算分数
score = testPaper.getScores().get(i);
} else if (result.equals("partially correct")) {
score = testPaper.getScores().get(i) / 2;
}
scores.add(score);
}
}
}
如果题目有效,调用 paper.checkAnswerWithResult(questionNum, answer) 核对答案并获取核对结果 result。然后根据这个结果来计算分值:
当 result.equals("true"),也就是答案完全正确时,通过 testPaper.getScores().get(i) 获取当前试卷中这道题设置的分值,并赋值给 score,即该题得满分。
当 result.equals("partially correct"),意味着答案是部分正确(适用于选择题、填空题的部分正确情况),按照规则将该题在试卷中设置的分值除以 2(score = testPaper.getScores().get(i) / 2;)来计算部分正确情况下的分值。如果答案是其他情况(比如错误等),score 初始化为 0,也就意味着该题得 0 分。最后将计算好的分值添加到 scores 列表中,用于后续汇总和输出。
4.改进方法
上述代码在单一职责设计原则上有很大的改进,我在主函数里进行区分处理输入信息,太过繁琐,可以存放到专门负责的函数或是类中。目前对于不同类型输入信息(题目、试卷、学生等)的解析逻辑都混在一起,显得比较冗长和复杂。可以将每种类型信息的解析逻辑抽取成独立的私有方法。
代码逻辑上,在 AnswerSheet 类的 saveAnswer 方法中,每次计算题目分值都需要通过 testPaper.getScores().get(i) 获取对应分值,当试卷题目较多时,这样的重复获取操作可能会有一定性能开销。可以考虑在循环外部获取一次分值列表并保存到局部变量中,然后在循环内直接使用这个局部变量,减少重复的方法调用开销,提升性能。
二.题目集五,7-1家居强电电路模拟程序
1.前言
知识点考查和难度:
本题开始考查继承多态的设计,电路设备的共性可以通过继承多态进行传递,这样的设计思路可以减少代码的复用,另外,区分输入信息是设备还是调档,电路设备如何建立连接传递电压,都是这个题目想要考察的思路难点。
2.设计分析:
首先是找出本题中的对象,对象间有什么共性取之作为一个父类,然后在子类中写其各自的方法。另外,考虑到设备各自的状态需要参考电压值,电路的连接和计算分配也是一个很重要的设计方向。
按照思路设计Device作为所有设备类的抽象基类,定义了设备共有的一些基本属性,同时声明了抽象方法 updateOutput,用于更新设备的输出状态。controldevice 类继承自 Device,作为一类特殊的设备基类,主要用于那些可以主动控制输出电压的设备
underconrolDevice 类同样继承自 Device,代表那些受输入输出电压差影响来更新自身状态的设备。设计串联电路类 SeriesCircuit,内部维护一个 List
类图如下:
该程序设计的时序图:
3.踩坑心得:
这题踩坑点在于,处理输入信息时总是处理不好。我开始写的代码总是将连接电路的信息处理出后,建立设备。总是将VCC和GND作为设备处理,然后得出错误的结果。最后通过筛查排除,将设备正确的建立。
点击查看代码
for (String line : lines) {
if (line.startsWith("[")) {
// 解析连接信息
String[] connections = line.substring(1, line.length() - 1).split(" ");
if (connections.length == 2) {
if (connections[0].equals("VCC")) {
// 处理电源连接情况,获取设备编号
} else if (connections[1].equals("GND")) {
// 如果连接到地,暂时不做特殊处理,可根据实际需求完善
} else {
circuit.addConnection(connections);
CircuitConnection connection = new CircuitConnection(); // 每次遇到一组连接关系创建一个新的CircuitConnection实例
// 普通设备连接情况,获取输入输出设备编号
String inputDeviceId = connections[0].substring(0, connections[0].indexOf("-"));
String outputDeviceId = connections[1].substring(0, connections[1].indexOf("-"));
char inputdeviceType = connections[0].charAt(0);
switch (inputdeviceType) {
处理
}
另一方面,对于调控信息的处理,我的代码运行出来调控的设备和电路里的不是同一个设备。为了解决这个问题,我学习到,创建连接电路存储已经建立的设备,然后如果有对设备的处理,需要先去查找是否建立,建立后再处理。
点击查看代码
Switch switchDevice = (Switch) circuit.devices.get(operation);
点击查看代码
class Circuit {
public Map<String, Device> devices = new HashMap<>();
private List<String[]> connections = new ArrayList<>();
public void addDevice(Device device) {
devices.put(device.getId(), device);
}
public void addConnection(String[] connection) {
connections.add(connection);
}
最后再输出结果,为了保证输出顺序,我对设备的顺序做了规定:
点击查看代码
List<Class<? extends Device>> order = Arrays.asList(
Switch.class,
FanSpeedController.class,
ContinuousSpeedController.class,
LightBulb.class,
FluorescentLight.class,
CeilingFan.class
);
4.改进方法
在 SeriesCircuit 类的 updateOutput 方法中,计算总电阻以及根据电阻分配电压等操作在每次更新电路状态时都会重复进行,如果电路结构相对稳定,这些计算结果在一定时间内是不变的。可以考虑添加缓存机制,将计算好的总电阻等结果缓存起来,下次更新时先判断电路结构是否变化,若未变化则直接使用缓存结果进行后续计算,减少重复计算带来的性能开销,提升性能。
三.题目集六,7-1家居强电电路模拟程序-2
1.前言
知识点考查和难度:
本题在上次基础上新增了并联电路,修改了信息的输入信息,新增了部分设备的阻值。难度体现在并联电路应该怎么处理,信息如何拆分,如何将这些已知的电路拆分出设备,并且将这些设备如何的建立连接关系。
2.设计分析
我的想法是整体法,将无论是给的串联信息或是并联电路,在拆分出具体的设备后,将整体看作一个普通的设备,它同样具有设备的电压差,阻值,输入输出电压等一般性。这样处理的话最后就是几个设备的串联,和上次题就有大致的相同方向。
具体设计并联电路类 ParallelCircuit:表示由多个串联电路组成的并联电路,内部包含一个 List
类图如下:
该程序的时序图:
3.踩坑心得
本题踩坑在,电路分析上。首先它的信息输入很复杂,有串联电路,其中串联电路中也有区别,连接VCC和GND的是主电路。其他的是一小部分分支。并联电路的信息中包含串联电路。
为此我总是没有分析好导致电路建立不起来。
点击查看代码
if (!deviceId.equals("VCC") &&!deviceId.equals("GND") && devicePattern.matcher(deviceId).matches()) {
Device device = circuit.devices.get(deviceId);
电压处理的先后性也是踩坑点:计算总电阻和按照电阻比例分配电压的过程涉及到对串联电路中所有设备的遍历和计算。需要正确处理不同类型设备(控制设备和非控制设备)的更新顺序和方式,以确保电压分配和状态更新符合串联电路的电学原理。对于控制设备,更新输出电压后,剩余电压会发生变化,这需要准确地更新剩余电压,以便后续为非控制设备分配正确的电压,这个过程中的计算错误可能会导致整个电路以及连接的其他电路状态更新错误。
点击查看代码
if (device instanceof underconrolDevice) {
device.updateOutput(); // 更新输出电压
underconrolDevice controlledDevice = (underconrolDevice) device;
controlledDevice.updatediffV();
controlledDevice.updateOutput();
}
4.改进方法
在 Circuit 类的 updateVoltages 方法中,同时进行了串联和并联电路电压更新、受控设备状态更新以及设备状态输出等多项操作,功能过于繁杂。可以将这些操作拆分成更小的、职责更单一的方法,比如 updateSeriesAndParallelVoltages 负责更新串联和并联电路的电压,updateControlledDevices 用于更新受控设备状态,printDeviceStates 专门进行设备状态输出等,通过方法调用的方式组织逻辑,提高代码的可读性和可维护性。
四.总结
从对类的组合到现在大作业的继承关系,我对面向对象设计有了更新的认识和体会。再学习了设计原则之后,我更懂得了合理的设计对代码的意义:促使代码结构更加清晰。当代码按照一定的原则进行组织,这对于代码审查时非常重要,降低了理解代码的难度。遵守设计原则在修改代码时能够最大程度地减少对其他部分的影响。使得在修改底层模块的实现时,高层模块可以不受干扰,从而方便了对软件的维护。但是我自己的设计还是很乱,很难达到一个比较清晰的设计,每次改动也需要很大幅度调整我的代码,还是得多多练习。