题目集7~8总结

前言

最后两次的java开发是面向对象特性进行模块化和抽象设计,这不仅强化了我们对Java语言基础的理解,还深入实践了面向对象设计、异常处理、多线程和复杂数据结构、递归和算法能力等核心知识点。以下是对这最后两次作业的总结和反思。
第一次作业
这是智能家居的第三次迭代,相比上次的题目,新增了互斥开关,这种开关有三个引脚,两个分支引脚和一个汇总引脚,允许只接通一个分支引脚,并且引入了限流电阻,防止短路,并且新增了受控设备,受控窗帘,在线路方面引入了新的电路结构,支持多条串联电路与并联电路的组合,这使得模拟更加复杂和现实。
第二次作业
这是智能家居最后一次迭代,新增了二极管,需要检查电流方向,如果电流从正向流入,则电阻为0,否则电流为0,要考虑电流限制检测,判断是否超过设备的最大允许电流,而且还要判断是否短路,线路相比上次的,可以实现并联的嵌套,即并联中也可以继续并联。需要考虑的情况也更加复杂了。

设计与分析

题目集7设计了以下十二个类,包括基础设备类,引脚类,普通开关设备类,互斥开关设备类,分档调速器类,连续调速器类,白炽灯类,日光灯类,吊扇类,落地扇类,受控窗帘类和电路连接类
1. Device类
定义所有设备的共同特性:
type: 设备类型,例如开关、灯、风扇等。
id: 设备编号。
pins: 引脚,用于连接其他设备,存储为一个 Map<Integer, Pin>。
抽象方法:
computeVoltage(): 计算设备的电压行为。
getOutput(): 输出设备的状态。
2. Pin类
定义了设备引脚的基本属性:
deviceIdentifier: 引脚所属设备的标识符。
pinNumber: 引脚编号。
voltage: 当前电压。
提供了辅助方法 getFullIdentifier(),生成引脚的完整标识符(如 K1-1 表示开关1的第1个引脚)。
3.SwitchDevice类
属性:
isClosed: 表示开关状态,true 为闭合,false 为断开。
方法:
toggle(): 切换开关状态。
computeVoltage(): 根据 isClosed 状态决定输出引脚的电压。
闭合:输出端电压与输入端相同。
断开:输出端电压为 0。
getOutput(): 返回状态。
4. MutuallyExclusiveSwitch类
属性:
connectTo2: 表示是否连接到 2。
方法:
toggle(): 切换连接状态。
computeVoltage(): 根据连接状态决定输出端电压。
getOutput(): 返回状态。
5.DiscreteSpeedController类
属性:
gear: 档位(0-3)。
方法:
increaseGear() 和 decreaseGear(): 调整档位。
computeVoltage(): 根据档位调整输出电压比例(0.0, 0.3, 0.6, 0.9)。
getOutput(): 返回档位状态。
6.ContinuousSpeedController类
属性:
parameter: 调节参数(0.0-1.0)。
方法:
setParameter(double param): 设置调节参数。
computeVoltage(): 按参数计算输出电压。
getOutput(): 返回调节参数状态。
7.IncandescentLight类
属性:
brightness: 光亮度(0-200lux)。
方法:
computeVoltage(): 根据电压差计算亮度,最大亮度为 200lux。
getOutput(): 返回亮度状态。
8.Daylight类
属性:
brightness: 光亮度(0 或 180lux)。
方法:
computeVoltage(): 根据电压差设置亮度。
getOutput(): 返回亮度状态。
9.CeilingFan类
属性:
speed: 转速(0-360rpm)。
方法:
computeVoltage(): 根据电压差计算转速。
getOutput(): 返回转速状态。
10.FloorFan类
属性:
speed: 转速(0, 80, 160, 260, 360rpm)。
方法:
computeVoltage(): 根据电压差设置固定转速。
getOutput(): 返回转速状态。
11.Curtain类
属性:
openPercentage: 开合比例(0%-100%)。
方法:
computeVoltage(): 计算开合比例。
setOpenPercentage(int percentage): 手动设置开合比例。
getOutput(): 返回开合状态。
12.Connection类
属性:
pins: 表示连接的引脚列表。
类图如下:
image

SourceMonitor报表如下:
image
由图可知代码行数总计579行,实际语句数量为371,分支语句占比27.2%,方法调用数量为143,带有注释的代码行比例为15.2%,定义的类和接口数量为8,每个类平均包含的方法数量为5.25,每个方法平均语句数量8.50,最复杂方法开始的行号为380,最复杂方法为CurtainDevice.computeVoltage(),直方图展示语句数量与嵌套深度的关系,大部分语句深度较浅(0-3层嵌套占据主要部分)。少数语句嵌套深度超过6层甚至9层,说明有些代码块的复杂性可能需要优化。
题目集8相比上次主要新增了二极管类,并对一些电路逻辑上进行了修改。
1.Diode类
属性
diodeState (String): 状态,"conduction" 或 "cutoff"。
方法
applyControlCommand(String cmd): 无操作。
updateState(): 根据电压方向更新状态。
getOutputString(): 输出二极管状态和电压差。
2.Circuit接口
方法
getCircuitId(): 返回电路 ID。
getEquivalentResistance(Map<String, Device> deviceMap): 计算等效电阻。
assignVoltageCurrent(double vIn, double vOut, Map<String, Device> deviceMap): 分配电压和电流
3. SerialCircuit类
属性
circuitId (String): 电路 ID。
connectionPairs (List<String[]>): 存储连接对。
方法
addConnectionPair(String pair): 添加连接对。
getEquivalentResistance(Map<String, Device> deviceMap): 计算串联总电阻。
assignVoltageCurrent(double vIn, double vOut, Map<String, Device> deviceMap): 分配电压和电流。
4. ParallelCircuit类
属性
circuitId (String): 电路 ID。
subCircuitIds (List): 子电路 ID 列表。
方法
addSubCircuit(String sid): 添加子电路 ID。
getEquivalentResistance(Map<String, Device> deviceMap): 计算并联总电阻。
assignVoltageCurrent(double vIn, double vOut, Map<String, Device> deviceMap): 分配电压和电流。
类图如下:
image

SourceMonitor报表如下:
image
由图可知,代码行数为649,总共有502个语句,在所有语句中,约25.3%的语句为分支语句,有82个方法调用语句,约14.6%的代码行包含注释,代码中定义了14个类和接口,每个类平均包含9.21个方法,每个方法的平均语句数为3.29,最复杂的方法位于第320行,最复杂的方法是 Main.getOutputString,块直方图显示了语句的深度分布。大部分语句位于0-2层。这段代码的复杂度不算特别高,但方法和类的数量相对较多,注释的覆盖率较低。

踩坑心得

1.第三次作业在输出时,白炽灯和受控窗帘输出没有符合预期,如下图所示:
image
白炽灯代码如下:

点击查看代码
// 白炽灯
    static class IncandescentLight extends Device {
        double brightness; // 0 ~ 200lux
        static final double MAX_VOLTAGE = 220.0;
        static final double MAX_BRIGHTNESS = 200.0;

        IncandescentLight(String type, int id) {
            super(type, id);
            // 两个引脚
            pins.put(1, new Pin(getIdentifier(), 1));
            pins.put(2, new Pin(getIdentifier(), 2));
            brightness = 0.0;
        }

        @Override
        void computeVoltage() {
            Pin pin1 = pins.get(1);
            Pin pin2 = pins.get(2);
            double voltageDiff = Math.abs(pin1.voltage - pin2.voltage);
            if (voltageDiff < 10.0) {
                brightness = 0.0;
            } else if (voltageDiff >= 10.0 && voltageDiff < MAX_VOLTAGE) {
                // 10V对应50lux,220V对应200lux
                brightness = ((voltageDiff - 10.0) / (MAX_VOLTAGE - 10.0)) * (MAX_BRIGHTNESS - 50.0) + 50.0;
                if (brightness > MAX_BRIGHTNESS) brightness = MAX_BRIGHTNESS;
            } else {
                brightness = MAX_BRIGHTNESS;
            }
        }

        @Override
        String getOutput() {
            return "@" + getIdentifier() + ":" + (int) brightness;
        }
    }

受控窗帘代码如下:

点击查看代码
        double totalLight; // 总光照强度

        CurtainDevice(String type, int id) {
            super(type, id);
            totalLight = 0.0;
        }

        @Override
        void computeVoltage() {
            Pin pin1 = pins.get(1);
            Pin pin2 = pins.get(2);
            double voltage = Math.abs(pin1.voltage - pin2.voltage);
            if (voltage < 50.0) {
                openPercentage = 100;
            } else {
                // 根据总光照强度设置开合比例
                if (totalLight < 50.0) {
                    openPercentage = 100;
                } else if (totalLight >= 50.0 && totalLight < 100.0) {
                    openPercentage = 80;
                } else if (totalLight >= 100.0 && totalLight < 200.0) {
                    openPercentage = 60;
                } else if (totalLight >= 200.0 && totalLight < 300.0) {
                    openPercentage = 40;
                } else if (totalLight >= 300.0 && totalLight < 400.0) {
                    openPercentage = 20;
                } else {
                    openPercentage = 0;
                }
            }
        }

        void setTotalLight(double light) {
            totalLight = light;
        }
    }

以上是受控窗帘和白炽灯的相关代码,逻辑上没有什么太大问题,受控窗帘方面对不同光照进行分类,来判断窗帘打开比例也是符合题意的,但是却相当于没有输出,但是开关状态又是正确的,所以感觉还是分压方面出现了问题,需要进行改进。
2.最后一次大作业里面出现短路检测,输入样例1的时候,却还是判断成了短路
image
短路判断代码如下:

点击查看代码
double R_total = mainCkt.getEquivalentResistance(deviceMap);
        if(R_total < 1e-9) {
            // 短路
            System.out.println("short circuit error");
            return;
        }

有关getEquivalentResistance代码如下:

点击查看代码
    public double getEquivalentResistance(Map<String, Device> deviceMap) {
        // 并联:1/R_eq = sum(1/R_i)
        double invSum=0;
        for(String cid: subCircuits) {
            // 可能是串联也可能并联
            if(Main.serialMap.containsKey(cid)) {
                SerialCircuit sc = Main.serialMap.get(cid);
                double r = sc.getEquivalentResistance(deviceMap);
                if(r < 1e-9) {
                    // 短路分支 => 并联等效≈0
                    return 0;
                }
                invSum += 1.0/r;
            } else if(Main.parallelMap.containsKey(cid)) {
                ParallelCircuit pc = Main.parallelMap.get(cid);
                double r = pc.getEquivalentResistance(deviceMap);
                if(r < 1e-9) {
                    return 0;
                }
                invSum += 1.0/r;
            }
        }
        if(invSum < 1e-9) {
            // 无支路或全∞ => R_eq=∞
            return 1e9;
        }
        return 1.0/invSum;
    }

如上是有关检测短路的代码,逻辑上差不多这样,但是即使不是短路,也会输出短路的信息,感觉可能还是其他地方有点问题,导致全输出短路,目前没有找到问题所在,也是导致得分不高,还是需要不断改进。

改进建议

多使用接口和抽象类,将共同方法提取到接口或抽象类中,提高代码复用性,创建计算电压的工具方法,将电压计算逻辑封装为一个独立方法,避免重复代码,创建输出格式化方法,通过方法统一设备状态输出格式,提高一致性,最后几次的样例不算多,有一些特殊情况需要自己不断去想,只有这样才能不断进步而且在写代码的时候应该多写一些注释,提高可读性,也避免过一段时间后,自己也不太清楚当时的含义了。

总结

随着最后一次大作业完成,本学期java课程也暂时告一段落了,这两次大作业还是不断去迭代,新增一些设备,但是最基础的还是最开始的框架上面不断开发,在此有一些收获和感慨如下,学习 Java 是一段充满挑战与收益的旅程,相比c语言,java 是一门强类型的面向对象语言,我深入了解了类、对象、继承、多态和封装等概念。通过实际编程实践,我体会到如何通过合理的类与对象设计来构建高内聚低耦合的程序,这让我对 OOP 有了更深的理解,在学习过程中,我掌握了 Java 提供的丰富标准库,包括集合框架(如 ArrayList、HashMap 等)、输入输出(I/O)、异常处理等。学习 Java 不仅让我掌握了一项技术,更让我理解了编程思维和解决问题的方法,总结来说,学习 Java 的过程是一个不断探索和发现的旅程,同时也是一个锻炼逻辑思维与创造力的过程。我期待在不断学习的过程中,能够运用 Java 更好地满足实际需求,解决更多复杂的问题,同时为我未来的职业发展打下坚实的基础。可能一些当时比较令人痛苦的东西(大作业),在未来回头看的时候,也许并没有那么痛苦了,也许当时的痛苦是为了更好的进步,对未来也会受益匪浅。Java课程可能结束了,但是学习是一个持久性工程,不会止步于此。

posted @ 2024-12-28 22:37  残柠梦  阅读(28)  评论(0)    收藏  举报