4~6次作业总结

一、前言

  1. 答题判题程序-4
  • 知识点:主要采用类的封装,将对象的属性和方法结合在一起,仅通过公共接口与外部进行交互,满足程序要求;通过try-catch语句来实现异常处理,当程序执行过程中遇到异常情况时,抛出一个异常对象,并由相应的catch块捕获并处理。
  • 题量分析:涉及到一个相对复杂的系统设计与实现,包含了多种类型的输入信息和处理逻辑。需要对各种输入信息进行解析处理和正确存储,以及对判分逻辑的实现。
  • 难度分析:本题的难度核心主要体现在两大方面:异常处理环节与新增的多选题处理部分。在异常处理方面,要求程序能够妥善应对多种复杂情况,包括但不限于输入顺序的混乱、题目的删除操作以及引用错误等,这极大地考验了程序的健壮性和容错能力。另一方面,新增的多选题部分则要求程序能够准确解析并处理包含多个选项的输入及判别,进一步增加了题目的挑战性。
  1. 家居强电电路模拟程序-1
  • 知识点:除了之前的类的封装,这次主要涉及到继承和多态。将电路也看成是一个设备,都继承设备类。其中每一个设备都有不同的输出,控制设备输出电压和自己的状态,受控设备输出属性,包括光照或转速。
  • 题量分析:需要识别控制命令和电路的关系,将两者分开识别解析
  • 难度分析:将所有的电路信息识别并存储后,按照要求计算出每一个受控设备的输出。总体难度不算大。
  1. 家居强电电路模拟程序-2
  • 知识点:与第二题类似,也运用了继承和多态,将串并联电路都继承设备类。
  • 题量分析:需要正确的识别控制命令和电路的关系。
  • 难度分析:主要难点在于电路中各个设备的连接,以及对断路和短路的处理。由于增加了并联电路,需要正确的识别两者,并将串联电路加入到并联电路中,在创建设备时由于每个设备都输入流两个引脚,需要正确的设置创建和加入电路顺序,保证各个设备之间的连接关系正确。此外,本题还引入了电阻,所以在串联和并联电路中,需要额外注意对输出电压的计算。

二、设计与分析

  1. 答题判题程序-4
    UML类图:
  • Topic类:表示单个题目,包含题号ID、内容content、标准答案standardAnswer。
  • TopicPaper类:试卷类,表示题目的集合,包含题目列表、题目数量。
    addTopic(Topic topic):向列表中添加一个新的主题。
    sortTopic():对列表中的主题进行排序。
    getTopicById(int id):根据提供的ID获取对应的主题信息
    judgeCorresponding(int topicNumber, String answer): 根据题目编号和答案判断是否匹配。
  • AnswerPaper类:答卷类,用于封装答题信息,包含试卷、答案列表、判题列表。
    addAnswer(String oneAnswer): 添加一个答案到answer列表。
    addResult(boolean oneResult): 添加一个结果到result列表。
    judge(): 判定答案是否正确。
    outputAll(): 输出所有的答案和结果。
  • FillInTheBlankQuestion:代表填空题。包含题目编号、内容和标准答案。可以判断答案是否正确。
  • Read类:表示读取器,主要负责输入数据的读入。增加了处理学生信息、答卷信息和删除题目信息的逻辑。
  1. 家居强电电路模拟程序-1
    UML类图
  • Device 类:作为所有具体设备的基类,定义了设备的基本属性和行为,包括标识符、引脚、输入源设备、输入电压、输出电压、电阻以及总电压等。定义了一个计算输出电压的方法 calculateOutputV(),但具体的实现需要由子类来完成。
  • ControlDevice、ControledDevice:分别表示控制设备和受控设备,仅用于区分。
  • ContinuousDimmer:连续调制器类,可以将输入电压根据不同的档位转换为不同的输出电压。
  • FanSpeedController:风扇调制器,作用与ContinuousDimmer类似,只不过档位不同
  • Switch:开关,新增了一个 state 属性,用来表示开关的状态(打开或关闭),实现了切换开关状态 (toggle()) 的方法。
  • FluorescentLight、fan、IncandescentLight:各种受控设备,各自有不同的输出方法。
  • SeriesCircuit:串联电路,包含了设备列表和顺序列表,并提供了添加设备、获取输出电压等功能。
  • DeviceController:整个系统的控制器,负责管理所有的电路和设备。包含了一系列与电路相关的操作方法,如解析连接、处理命令、创建设备等,使用了Map<String, SeriesCircuit>来存储串连电路的信息。
  1. 家居强电电路模拟程序-2
    UML类图:


    增加了以下内容
  • ParallelCircuit:并联电路,同样包含了设备列表和顺序列表,并提供了类似的操作方法。
  • DeviceController:整个系统的控制器,负责管理所有的电路和设备。包含了一系列与电路相关的操作方法,如解析连接、处理命令、创建设备等,使用了Map<String, SeriesCircuit>和Map<String, ParallelCircuit>来存储串并连电路的信息。
  • Lfan:落地扇,受控设备,根据输入的电压大小有不同的输出转速

三、踩坑心得及改进

  1. 第四次
  • 异常处理
    在读入数据及对数据的处理中判断输入数据是否符合约束条件,如果不符合,在processLine中会抛出一个异常,在检测到这个异常后,程序会运行errorMessages.add("wrong format:" + line);这条代码,以找出错误。
  • 输出顺序的处理
// 按学号和试卷号排序学生答卷
answerSheets.sort(Comparator.comparing(AnswerPaper::getStudentId)
        .thenComparing(AnswerPaper::getPaperId));

将所有信息都存进答卷中,首先根据 studentId 排序,如果 studentId 相同,则根据 paperId 进行排序。这样可以确保输出的顺序是学生ID升序排列的,并且在同一个学生的多个试卷中按照试卷号进行排列。

// 处理每份答卷
for (AnswerPaper answerSheet : answerSheets) { // 使用已排序的列表处理输出顺序
    String paperId = answerSheet.getPaperId();
    // 省略逻辑...
}

这部分循环遍历已经排好序的 answerSheets 列表,逐个处理每份答卷,确保最终输出的成绩报告有序。
改正完成后,前后对比图如下:

  1. 第五次作业
  • 结果的精确度问题
    在计算设备的输出电压时,我没有使用double类型
    在device中将改正以下代码public abstract double getOutput(double inputVoltage);
    让getOutput的返回值改为double,并修改相关子类的返回类型,完成后问题得以解决。
  1. 第六次作业
  • 解决电路的连接问题
    在处理并联电路时创建了一个M设备,但处理最后一个串联电路时也需要创建M,但此时若两者都创建了新的实例,在后续调用时会出现匹配不上的现象,导致我在之后寻找设备的输入源时,并联电路的输入源总是空的,改进如下:
    在并联电路的处理部分添加以下代码
if (parallelCircuit.containsKey(parallelCircuitId)) {
        	newparallelCircuit = parallelCircuit.get(parallelCircuitId);
        } else {
            // 如果不存在,则创建一个新的并联电路实例
        	newparallelCircuit = new ParallelCircuit(parallelCircuitId, "", "");
            parallelCircuit.put(parallelCircuitId, (ParallelCircuit) newparallelCircuit);
        }

判断如果电路不存在,则创建一个新的并联电路实例,否则使用之前创建好的实例。
同时,在createDevice方法中也加入以下代码判断

else if (deviceId.startsWith("M")) {        	
        	String suffix = pin.split("-")[1]; // 提取后缀(IN 或 OUT)
            if (suffix.equals("IN")) {
                // 对于 M1-IN 设备,使用已存在的并联电路实例
            	if (parallelCircuit.containsKey(deviceId)) {
                    device = parallelCircuit.get(deviceId);
                } else {
                    // 如果不存在,则创建一个新的并联电路实例
                	device = new ParallelCircuit(deviceId, pin, "");
                    parallelCircuit.put(deviceId, (ParallelCircuit) device);
                }
            } else{
                // 对于 M1-OUT 设备,创建新的并联电路实例
            	device = new ParallelCircuit(deviceId, pin, "");
                parallelCircuit.put(deviceId, (ParallelCircuit) device);
            }
        
        }

这样可以保证使用的并联电路始终是同一个

可以看到改完后M1的inputSources成功连上了ContinuousDimmer。

  • 处理电路的断路问题
    给控制设备开关设一下电阻,当开关打开时电路为断路状态,此时电阻设为无穷大,否则电阻为0。
  • 处理电路的短路问题
    若电路处于短路,此时电流会是无穷大,在计算输入电压的方法中加入以下代码,将短路部分分开处理
else {
                    if (I != Double.POSITIVE_INFINITY) {
                        outputV = device.getInputSources() != null ? device.getInputSources().getOutputV() - I * device.getR() : 0;
                    } else {
                        outputV = device.getInputSources() != null ? device.getInputSources().getOutputV() : 0;
                    }
                }

输入样例为

#T1:[IN K1-1] [K1-2 OUT]
#T2:[IN K2-1] [K2-2 D1-1] [D1-2 OUT]
#M1:[T1 T2]
#T3:[VCC L1-1] [L1-2 M1-IN] [M1-OUT D3-1] [D3-2 GND]
#K1
#K2
#L1:0.80
end

此时电源是短路的,前后输出对比图如下:

  • 将总电压变成调速器的输出电压
    如果调速器改变了电压的输出,此时不能再使用220V作为电路的总电压
点击查看代码
 // 遍历主电路,检查是否存在调速器
        boolean hasFanSpeedController = false;
        for (String key : mainCircuit.getDeviceOrder()) {
            Device device = mainCircuit.getDevices().get(key);
            if (device instanceof FanSpeedController||device instanceof ContinuousDimmer) {
                hasFanSpeedController = true; // 发现调速器
                break;
            }
        }

        // 如果有调速器,获取它的输出电压作为总电压
        if (hasFanSpeedController) {
            for (String key : mainCircuit.getDeviceOrder()) {
                Device device = mainCircuit.getDevices().get(key);
                if (device instanceof FanSpeedController||device instanceof ContinuousDimmer) {
                    totalV = device.getOutput(device.getInputSources() != null ? device.getInputSources().getOutputV() : 220);
                    break; // 找到第一个调速器后退出
                }
            }
        }
加入两段这样的代码,找到电路中的调速器,并根据调速器的输出电压重新计算电路中的总电流,此时当我将调速器的档位调制0.8时,程序也可以正常输出了。

这几次实验中遇到的问题及解决总结如下:

  1. 异常处理,数据读入和处理过程中,我加入了严格的异常检测机制,确保输入数据符合预设的约束条件。一旦检测到异常数据,程序会记录错误信息,并继续处理后续数据,从而提高了程序的健壮性。
  2. 电路实例不匹配,需要仔细分析电路的连接方式,采取相应的处理措施,保证不会出现创建多个实例的情况
  3. 需要仔细分析电路的连接方式、断路与短路情况,针对于这些特殊情况分开处理。

四、总结
通过这几次的作业实践,我认识到了面向对象编程中类的封装、继承与多态等核心概念的作用。在答题判题程序的设计中,我地运用了Topic类、TopicPaper类、AnswerPaper类等结构,将题目的各项属性和操作逻辑封装在一起,并通过公共接口与外部世界进行简洁明了的交互。这种设计模式不仅显著提升了代码的可读性和可维护性,还为后续的扩展和修改预留了充足的空间。

而在家居强电电路模拟程序的开发过程中,我则充分利用了继承和多态的特性,将各类设备和电路都统一在Device类这一基类之下。通过定义抽象的接口和具体子类的实现,我实现了代码的复用和灵活扩展。无论是控制设备、受控设备,还是串联电路、并联电路,都能够通过统一的接口进行操作,极大地简化了程序的复杂度。同时我也深刻认识到了异常处理在编程中的重要性。在数据读入和处理的过程中,我加入了严密的异常检测机制,确保每一份输入数据都符合预设的约束条件。一旦检测到异常数据,程序会立即记录错误信息,并继续处理后续数据,从而保证了程序的健壮性和容错能力。这种设计使得程序在面对复杂多变的输入情况时,依然能够稳定运行并输出正确的结果。

在实践的过程中,我也发现了自己的一些不足之处。在处理各种极端情况时,我往往缺乏足够的分析和预见,导致程序在这些情况下容易出现错误。如果测试样例取到极限值,我很难找到问题出在什么地方。我需要更加注重对极端情况的考虑和分析,以提升自己的问题解决能力和代码质量。

posted @ 2024-11-23 21:08  段诗琪  阅读(53)  评论(0)    收藏  举报