第四次大作业到第六次大作业总结

第四次大作业到第六次大作业总结

一、前言

难度分析

  • 第四题集:第四次大作业在前三次基础上进行迭代,难度较大,得分较低。

  • 第五题集:第五次大作业是新的一次命题,难度适中,得分较高。

  • 第六题集:第六次大作业只有一题,虽然是第五次的迭代,但难度提升较大,更加贴近现实要求,得分不高。

    三次实验,第四次与前三次实验相关,也是在前三次基础上进行迭代,难度相比之前有较多的提高,主要问题还是在对输入内容的读取,代码始终无法对输入部分进行准确的判断,面对多选题时,无法准确判断半对的情况,对于多张试卷的处理依旧存在很大的问题。在第五次作业中,因为是一个崭新的开始,在系统创建中采用模块化设计,充分利用了面向对象的特性,通过继承和多态性来实现不同设备的具体行为。通过使用集合和映射结构,系统能够快速访问和更新设备状态,并确保设备按编号排序。第六次作业总感觉难度提升有点大。


二、设计与分析

2.1 第四次题集

  • 类设计
    Question 类:用于表示一个题目,包含题目ID、题干、正确答案以及题目类型。提供了方法来检查答案是否正确以及计算得分。

ExamPaper 类:用于表示一张试卷,包含试卷ID和题目及其对应分数的映射。提供了添加题目和计算总分的方法。

AnswerSheet 类:用于表示学生的答题纸,包含试卷ID、学生ID、学生姓名以及学生的答案。提供了添加答案的方法。

  • 功能实现分析
    题目管理:代码通过解析以 #N:、#Z:、#K: 开头的行来管理题目。每个题目包含题目ID、题干、答案和题目类型(选择题、填空题、简答题)。使用 Map<Integer, Question> 存储题目信息,以题目ID为键,便于快速查找。
    试卷管理:解析以 #T: 开头的行来管理试卷。每张试卷包含一个试卷ID和若干题目的ID及其对应分数。使用 Map<Integer, ExamPaper> 存储试卷信息,以试卷ID为键。检查每张试卷的总分是否为100分,并在不符合时输出警告。
    学生信息管理:解析以 #X: 开头的行来管理学生信息。每个学生包含一个学生ID和姓名。使用 Map<Integer, String> 存储学生信息,以学生ID为键。
    答题记录管理:解析以 #S: 开头的行来管理学生的答题记录。每个答题记录包含试卷ID、学生ID和学生的答案。使用 List 存储答题记录,方便后续的评分和排序。
    题目删除:解析以 #D: 开头的行来记录被删除的题目ID。使用 Set 存储被删除的题目ID,方便在评分时检查题目是否有效。
    评分与结果输出:对每张答题纸进行评分,首先根据试卷ID和学生ID对答题纸进行排序。对每道题目,根据题目类型和学生的答案进行评分。
    选择题:答案必须与标准答案完全一致。
    填空题:支持多个正确答案,学生答案只需匹配其中之一。
    简答题:支持部分正确的评分机制。
    输出每道题的结果,包括题干、学生答案、正确性状态,以及学生的总分。
    错误处理:对于格式错误的输入,输出提示信息。对于不存在的试卷、题目、学生,或被删除的题目,提供相应的提示信息。

  • PowerDesigner类图

2.2 第五次题集

  • 类设计
    Control 类:这是一个基类,定义了输入电压和输出电压的基本属性,以及更新输出电压的方法。其他设备继承自这个类,重写 updateOutputVoltage 方法以实现特定设备的功能。

SwitchControl 类:继承自 Control,实现了一个简单的开关控制。开关有两个状态:打开和关闭,通过 toggleState 方法切换状态。输出电压根据开关状态更新。

FenDerive 类:继承自 Control,实现了一个分档调速器,可以在0到3档之间调节。每档对应不同的输出电压比例。

LianDeriver 类:继承自 Control,实现了一个连续调速器,输出电压可以在0到输入电压之间连续变化。

Device 类:这是一个设备基类,定义了电压差的基本属性。具体设备如 Fan、IncandescentLamp 和 FluorescentLamp 继承自这个类,并实现各自的功能,如计算速度或亮度。

DeviceManager 类:负责设备的管理和连接。它维护了多个 TreeMap,分别存储不同类型的设备,并提供方法来处理设备连接、控制命令以及输出设备状态。

  • 功能实现
    设备连接管理:通过 processConnection 方法解析连接信息,将设备按顺序连接在电路中。连接信息被存储在 connections 列表中。

设备初始化:initializeDevices 方法遍历连接列表,从 VCC 开始设置设备的输入电压,并根据连接关系更新设备的输出电压。

设备控制:processControl 方法解析控制命令,更新设备的状态或参数(如开关状态、调速器档位或范围),并更新连接设备的输出电压。

状态输出:outputDeviceStatus 方法按设备类型和编号顺序输出每个设备的状态信息。

  • 扩展性
    新增设备类型:可以通过继承 Control 或 Device 类轻松添加新类型的设备,只需实现特定的功能逻辑。

新增控制命令:可以在 processControl 方法中添加新的命令解析逻辑,支持更多的设备操作。

  • PowerDesigner类图

2.2 第六次题集

  • 类设计
    Control 类及其子类:Control 是一个抽象基类,定义了基本的输入和输出电压属性,以及更新输出电压的抽象方法。子类 SwitchControl、FenDerive 和 LianDeriver 实现了具体的控制逻辑。
    SwitchControl 实现了开关控制,具有开关状态的切换功能。
    FenDerive 实现了分档调速器,支持四档调节。
    LianDeriver 实现了连续调速器,输出电压可以在0到输入电压之间连续变化。
    Device 类及其子类:Device 是一个抽象基类,定义了电压差的基本属性和获取设备状态的抽象方法。子类 IncandescentLamp、FluorescentLamp、Fan 和 FloorFan 实现了具体的设备功
    能。子类通过实现 getStatus 方法来返回设备的状态,如亮度或转速。
    电路类:ConnectionPoint 表示电路中的连接点。SeriesCircuit 和 ParallelCircuit 分别表示串联电路和并联电路,支持复杂电路结构的表示。

DeviceManager 类:负责管理和控制所有设备及其连接。使用多个 TreeMap 来存储不同类型的设备,按编号排序,便于管理和输出。提供方法来处理电路连接、控制命令、设备初始化和状态输出。

  • 功能实现分析
    电路连接管理:processConnection 方法解析电路连接信息,支持串联和并联电路的定义。processMainCircuit 方法识别主电路,用于后续的电压计算。

设备初始化:initializeDevices 方法根据电路连接关系,递归计算并设置设备的输入电压。使用 traverseCircuit 方法遍历电路,处理控制设备和受控设备的电压更新。

控制命令处理:processControl 方法解析控制命令,支持对开关、分档调速器和连续调速器的操作。applyControls 方法应用控制命令,并重新初始化设备以反映命令的变化。

状态输出:outputDeviceStatus 方法按设备类型和编号顺序输出每个设备的状态信息。

  • 设计特点
    面向对象设计:通过类的继承和多态,实现了设备和控制器的多样性和通用性。基类 Control 和 Device 提供了基本的属性和方法,具体子类实现各自的逻辑。

灵活的电路管理:支持复杂的串联和并联电路结构,能够处理多种设备的连接和控制。

使用集合类:TreeMap 用于存储设备,按键(设备编号)自动排序,方便设备的按序输出。

  • PowerDesigner类图

三、采坑心得

3.1 第四次题目集

1.源码提交过程中的问题

  • 问题1:对于多选题无法准确判断答案的对错,即对于半对情况无法正确处理

  • 部分源码分析

public int calculateScore(String userAnswer, int maxScore) {
    if (checkAnswer(userAnswer)) {
        return maxScore;
    } else if (type.equals("#Z:")) {
        Set<String> correctAnswers = new HashSet<>(Arrays.asList(answer.split(" ")));
        Set<String> userAnswers = new HashSet<>(Arrays.asList(userAnswer.split(" ")));
        if (!correctAnswers.equals(userAnswers) && !Collections.disjoint(correctAnswers, userAnswers)) {
            return maxScore / 2;
        }
    }
    return 0;
}
public String getPartialCorrectStatus(String userAnswer) {
    if (type.equals("#Z:")) {
        Set<String> correctAnswers = new HashSet<>(Arrays.asList(answer.split(" ")));
        Set<String> userAnswers = new HashSet<>(Arrays.asList(userAnswer.split(" ")));
        if (correctAnswers.containsAll(userAnswers) && userAnswers.size() < correctAnswers.size()) {
            return "partially correct";
        }
    }
    return "false";
}
  • 在 calculateScore 方法中

  • 首先检查用户的答案是否完全正确,如果是,则返回满分。

  • 对于多选题(type.equals("#Z:")),如果用户答案与正确答案不完全相同,但存在交集(即用户答案中包含部分正确答案),则返回一半的分数。

  • 在 getPartialCorrectStatus 方法中

  • 对于多选题(type.equals("#Z:")),如果用户的答案是正确答案的子集(即用户选择了部分正确答案,但未选择全部),则返回 "partially correct"。否则,返回 "false"。

  • 这两个方法结合在一起处理了多选题部分正确答案的情况,通过计算部分得分和提供部分正确的状态信息。

3.2 第五次题目集

1.源码提交过程中的问题

  • 问题1:对于已有测试案例全部通过,未找出具体错误原由。

  • 部分源码分析

public void initializeDevices() {
    double currentVoltage = 220.0;

    for (String[] connection : connections) {
        String firstPin = connection[0];
        String secondPin = connection[1];

        // If firstPin is VCC, set inputVoltage to control device
        if (firstPin.equals("VCC")) {
            if (secondPin.startsWith("K")) {
                int deviceNumber = Integer.parseInt(secondPin.substring(1, secondPin.indexOf('-')));
                switches.putIfAbsent(deviceNumber, new SwitchControl());
                switches.get(deviceNumber).setInputVoltage(currentVoltage);
            }
            // Similar logic for other device types (F, L, B, R, D)
        }
        // Logic for other pin types (K, F, L, etc.)
    }
}
public void processControl(String line) {
    if (line.startsWith("#K")) {
        int deviceNumber = Integer.parseInt(line.substring(2));
        SwitchControl sw = switches.get(deviceNumber);
        if (sw != null) {
            sw.toggleState();
            double outputVoltage = sw.getOutputVoltage();
            // Update all devices connected to this switch
        }
    }
    // Similar logic for other control types (F, L)
}
  • 在 initializeDevices 方法中

  • 功能:initializeDevices 方法负责根据连接信息初始化各个设备的输入电压。

  • 逻辑:假设电源电压为 220V(currentVoltage)。遍历 connections 列表,解析每个连接。如果连接的第一个端子是 VCC,则将电压传递给相应的控制设备(如开关、分档调速器、连续调速器等)。使用 putIfAbsent 方法确保每个设备只被初始化一次。根据设备类型(通过设备标识符的前缀如 "K", "F", "L" 等)来创建并设置设备的输入电压。

  • 在 processControl方法中

  • 功能:processControl 方法处理输入的控制命令,更新设备状态。

  • 逻辑:检查命令类型(开关、分档调速器、连续调速器)。根据命令类型和编号获取对应的设备实例。执行设备特定的状态切换或调整操作(如切换开关状态、调整调速器档位)。调用设备的 updateOutputVoltage 方法更新其输出电压。更新所有连接到该设备的设备的电压差。

3.3 第六次题目集

1.源码提交过程中的问题

  • 问题1:在电压更新逻辑上有问题
  • 部分源码分析
private void traverseCircuit(String circuitId, double inputVoltage) {
    if (circuitId.startsWith("T")) {
        SeriesCircuit sc = seriesCircuits.get(circuitId);
        if (sc == null) return;

        double currentVoltage = inputVoltage;

        for (ConnectionPoint cp : sc.connections) {
            String deviceId = cp.deviceId;
            String pinName = cp.pin;
            String deviceType = getDeviceType(deviceId);
            int deviceNumber = getDeviceNumber(deviceId);

            // 处理特殊引脚
            if (deviceId.equals("VCC") || deviceId.equals("GND") || deviceId.equals("IN") || deviceId.equals("OUT")) {
                continue;
            }

            if (deviceNumber == -1) {
                continue;
            }

            // 处理控制设备
            if (deviceType.equals("K")) {
                switches.putIfAbsent(deviceNumber, new SwitchControl());
                SwitchControl sw = switches.get(deviceNumber);
                sw.setInputVoltage(currentVoltage);
                currentVoltage = sw.getOutputVoltage();
            } else if (deviceType.equals("F")) {
                fenDerives.putIfAbsent(deviceNumber, new FenDerive());
                FenDerive fen = fenDerives.get(deviceNumber);
                fen.setInputVoltage(currentVoltage);
                currentVoltage = fen.getOutputVoltage();
            } else if (deviceType.equals("L")) {
                lianDerivers.putIfAbsent(deviceNumber, new LianDeriver());
                LianDeriver lian = lianDerivers.get(deviceNumber);
                lian.setInputVoltage(currentVoltage);
                currentVoltage = lian.getOutputVoltage();
            } else {
                // 处理受控设备
                double deviceVoltage = currentVoltage;
                switch (deviceType) {
                    case "B":
                        incandescentLamps.putIfAbsent(deviceNumber, new IncandescentLamp());
                        IncandescentLamp lampB = incandescentLamps.get(deviceNumber);
                        lampB.setVoltageDifference(deviceVoltage);
                        break;
                    case "R":
                        fluorescentLamps.putIfAbsent(deviceNumber, new FluorescentLamp());
                        FluorescentLamp lampR = fluorescentLamps.get(deviceNumber);
                        lampR.setVoltageDifference(deviceVoltage);
                        break;
                    case "D":
                        fans.putIfAbsent(deviceNumber, new Fan());
                        Fan fan = fans.get(deviceNumber);
                        fan.setVoltageDifference(deviceVoltage);
                        break;
                    case "A":
                        floorFans.putIfAbsent(deviceNumber, new FloorFan());
                        FloorFan floorFan = floorFans.get(deviceNumber);
                        floorFan.setVoltageDifference(deviceVoltage);
                        break;
                    default:
                        System.err.println("未知设备类型: " + deviceType);
                }
            }

            // 检查是否是并联电路的入口
            if (pinName.equals("IN")) {
                String parallelId = deviceId;
                ParallelCircuit pc = parallelCircuits.get(parallelId);
                if (pc != null) {
                    for (SeriesCircuit subSc : pc.seriesCircuits) {
                        traverseCircuit(getCircuitId(subSc), currentVoltage);
                    }
                }
            }
        }

    } else if (circuitId.startsWith("M")) {
        ParallelCircuit pc = parallelCircuits.get(circuitId);
        if (pc == null) return;

        for (SeriesCircuit sc : pc.seriesCircuits) {
            traverseCircuit(getCircuitId(sc), inputVoltage);
        }
    }
}
  • 在 traverseCircuit方法中

  • 电路类型判断:

该方法首先根据 circuitId 判断当前处理的是串联电路(T 开头)还是并联电路(M 开头)。
串联电路处理:

对于串联电路,方法遍历其中的每一个 ConnectionPoint。
对于每个设备,根据其类型(如开关、调速器、灯具等)设置输入电压并计算输出电压。
如果设备是控制设备(如开关或调速器),则使用其输出电压作为下一个设备的输入电压。
并联电路处理:

对于并联电路,方法遍历其中的每一个串联电路。
每条串联电路都接收到相同的输入电压。
设备状态更新:

对于受控设备(如灯具和风扇),直接设置其电压差,而不改变 currentVoltage。
递归调用:

当遇到并联电路的入口时(IN 引脚),递归调用 traverseCircuit 方法处理并联电路的每一条串联支路。

四、改进建议

4.1 对编程题目的改进意见

输入验证和错误处理:

增加对输入的更多验证,以确保输入的格式和内容符合预期。例如,验证学生ID和题目ID是否存在,确保分数是有效的整数等。处理异常情况,例如输入格式错误时抛出异常并提供有意义的错误信息。代码结构和可读性:将主方法中的逻辑拆分为多个小方法,每个方法负责一个特定的功能(如处理题目输入、处理试卷输入、处理学生输入等)。使用有意义的变量和方法名,以提高代码的可读性。使用集合框架的最佳实践:对于 questionMap 和 examPaperMap,可以考虑使用 ConcurrentHashMap 以提高线程安全性(如果有多线程需求)。使用 LinkedHashMap 代替 HashMap,如果需要维护插入顺序。

4.2 对编程题目的改进意见

面向对象设计

多态性:考虑为 Device 类添加一个抽象方法 updateStatus,然后在每个子类中实现,以减少在 DeviceManager 中的重复代码。接口或抽象类:为控制设备(如 SwitchControl, FenDerive, LianDeriver)创建一个接口或抽象类,以统一处理电压更新逻辑。

设计模式上:

1.观察者模式:如果设备状态变化需要通知其他组件,可以考虑使用观察者模式来实现这种通知机制,但对于这些模式还是不胜了解。
2.命令模式:对于控制命令的处理,可以使用命令模式,将每个控制命令封装为一个对象,以提高代码的灵活性。

4.3 对编程题目的改进意见

并联电路电压处理:

1.确保在并联电路中,每个支路都能接收到相同的输入电压。在当前实现中,可能由于递归逻辑或电压传递不当,导致并联电路中的设备没有正确分配到电压。
在处理并联电路时,可以记录并输出每个支路接收到的电压,以确保逻辑正确。
电压传递逻辑:

2.在 traverseCircuit 方法中,确保每个设备在设置输入电压后,正确更新其输出电压,并将其作为下一个设备的输入电压。
检查 updateOutputVoltage 方法的实现,确保每个设备根据其当前状态正确计算输出电压。

3.在 applyControls 方法中,确保在应用控制命令后,所有受控设备(如灯具和风扇)都能根据新的电压差更新其状态。
可以在每次状态更新后输出设备的电压差和状态,以便调试。


五、总结

对本阶段三次题目集的总结

对于第五次题目,代码模拟了一个设备管理系统,用于管理各种电气设备及其控制机制。该系统设计用于处理不同类型的设备,如开关、调速器、白炽灯、日光灯和风扇。使用了工厂模式来创建和管理不同类型的设备和控制器根据输入的设备类型和编号,动态地创建实例并存储在相应的集合中。也有用到策略模式,使得各种设备通过继承Device类,并实现自己的逻辑来计算速度或亮度。这些设备类可以看作是不同的策略,通过多态性在运行时选择合适的策略来执行。

对于第六次题目,代码是一个设备管理系统,能够处理各种设备(如开关、灯具、风扇等)之间的连接和控制。虽然功能上基本实现了需求,但在处理复杂电路时,特别是并联电路的电压分配和设备状态更新方面,但还是存在一些问题。并联电路中的电压分配可能不准确,导致设备状态(如风扇转速)不符合预期。在应用控制命令后,设备状态更新可能没有正确反映电路的变化。在这次代码中虽然没有直接实现工厂模式,但代码中有类似工厂模式的元素。例如,在 traverseCircuit 方法中,根据设备类型创建不同的设备对象(如 SwitchControl、FenDerive、LianDeriver 等),可以进一步抽象为工厂方法。虽然代码中没有显式使用某些设计模式,但通过这些编程方法和设计原则,代码实现了较好的模块化和灵活性。通过进一步重构,可以更清晰地实现一些设计模式,如工厂模式或观察者模式,以增强代码的可维护性和可扩展性。


posted @   傠瞂頫  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示