JAVA第三次博客作业

一、前言

总结两次次题目集的知识点、题量、难度等情况

第七次作业 家居强电电路模拟程序-3
  本次迭代在“家居强电电路模拟程序-2”的基础上增加了互斥开关,为避免短路,互斥开关设置了限流电阻,12引脚之间默认电阻为5,13引脚之间默认电阻为10;增加了受控窗帘,受控窗帘的电路符号为S,其最低工作电压为50V,电压达到或超过50V,窗帘即可正常工作,不考虑室外光照强度和室内空间大小等因素,窗帘受室内灯光的光照强度控制;本次迭代还考虑多个并联电路串联在一起以及一条串联电路中包含其他串联电路的情况。

 主要知识点:

  • 面向对象编程(OOP):设计电路设备类、受控设备类、控制设备类、串联电路类和并联电路类等。
  • 电路模拟:模拟开关、调速器、灯具、风扇和窗帘等设备的工作状态和参数。
  • 数据结构:处理和存储设备信息、连接信息、控制设备调节信息等。
  • 算法设计:实现电路连接和调节的逻辑,包括串联和并联电路的处理。
  • 输入输出处理:解析输入信息,生成和格式化输出信息。
  • 数值计算:计算电压、电流、电阻等电路参数,并应用截尾规则处理小数。
  • 异常处理:确保电路连接不会造成短路等异常情况。
  • 用户交互:根据用户输入调整电路状态,并提供反馈。

第八次作业 家居强电电路模拟程序-4

  本次迭代在“家居强电电路模拟程序-3”的基础上增加了管脚电压的显示;对设备通过的电流限制,实时电流超过最大电流就会输出相应提示;如果出现短路状态,所有元器件信息不输出,仅输出提示信息;并联电路中会包含并联电路;增加二极管元件,正向导通,反向截止。

 主要知识点:

  • 数值计算:递归计算电路中的电压、电流等参数。
  • 错误检测:检测电路中的短路情况,并给出相应的错误提示。
  • 电流限制:为每个设备设置最大电流限制,当电流超过限制时,输出错误信息。
  • 二极管特性:模拟二极管的正向导通和反向截止特性,并在电路计算中考虑这一点。

题量&难度:题目经过每次迭代,其代码量和难度都会逐渐增加,尤其是最后一次大作业,要考虑的情况更多更复杂。

二、设计与分析

重点对题目的提交源码进行分析,可参考SourceMontor的生成报表内容以及PowerDesigner的相应类图,要有相应的解释和心得(做到有图有真相)。

第七次作业 家居强电电路模拟程序-3

设计类图如下:

 在这个类图中:

  • Main 类:作为程序的入口点,负责初始化 TestParser 对象并调用其方法来解析输入并打印电路设备的状态。
  • Circuit 类:是一个抽象类,代表电路的基本结构。它包含设备列表、连接信息和状态标志。提供了创建设备、添加连接和判断设备类型的方法。
  • CircuitDevice 类:是电路中所有设备的基类,定义了设备的基本属性,如输入电压、输出电压、电阻和电流。
  • ControlDevice 类:继承自 CircuitDevice,代表控制设备,如开关和调速器。提供了获取输出电压的方法。
  • ControlledDevice 类:同样继承自 CircuitDevice,代表受控设备,如灯具、风扇和窗帘。定义了获取设备状态的抽象方法。
  • Device_K 、Device_H 、Device_F 、Device_L 类:分別代表不同类型的控制设备,继承自 ControlDevice。
  • Device_B, Device_R, Device_D, Device_A, Device_S 类:分别代表不同类型的受控设备,继承自 ControlledDevice 。
  • MajorCircuit 类:继承自 Circuit,代表主电路。提供了计算电路电阻和电压差的方法。
  • ParallelCircuit 类:也继承自 Circuit,代表并联电路。提供了计算并联电路电阻的方法。
  • SeriesCircuit 类:同样继承自 Circuit,代表串联电路。提供了计算串联电路电阻和电流的方法。
  • TestParser 类:负责解析输入的电路信息,包括串联和并联电路的连接信息,以及控制设备的调节信息。它还负责计算电路的电阻、电压差,并打印出所有设备的状态。

主要逻辑和详细设计:

1.程序启动:

  Main 类的 main 方法作为程序的入口点,初始化 TestParser 对象。

2.输入解析:

  TestParser 的 runParsing 方法开始工作,它首先调用 parseInput 方法来读取用户输入的电路配置信息。  

3.电路构建:

  parseConnections_T 方法解析串联电路(SeriesCircuit)的连接信息,parseConnections_M 方法解析并联电路(ParallelCircuit)的连接信息。这些方法会创建相应的电路对象,并将设备添加到电路中。

4.设备创建:

  在解析连接信息的过程中,如果遇到新的设备ID,createDevice 方法会被调用来创建具体的设备对象(如 Device_K, Device_F 等),并将其添加到电路的设备列表中。

  这里主要说明一下对于互斥开关的创建,因为互斥开关比较复杂,其拥有的2、3两个引脚对应着两个电路,

复制代码
class Device_H extends ControlDevice {
    private int status; // 开关状态,0开  1关  最后输出这个
    private int status2=1; //针对引脚2
    private int status3=0; //针对引脚3
    private double resistance2=5;
    private double resistance3=10;

    public Device_H(String id) {
        super(id,0);
        this.status = 0; // 初始状态为开
        this.resistance=resistance2;
    }

    @Override
    public void calculateOutputVoltage() {}//不单独计算,互斥开关的输出电压 只能和受控设备一起计算
    

    //0/1之间切换状态  默认状态为1、2引脚接通,1、3引脚断开。
    public void toggleSwitch() {
        status = (status + 1) % 2; // 切换状态
        if(status==0){//此时就是引脚2连通
            this.resistance=resistance2;
            status2=1;
            status3=0;
        }else{
            this.resistance=resistance3;
            status2=0;
            status3=1;
        }
    }
}
复制代码

  并且,为了获得某一电路中互斥开关所在引脚是2、3 ?我在Circuit基础电路类里面设置了一个Map<String, Integer> branchflags;用来标识该电路是互斥开关2引脚所在电路,还是3引脚所在电路。

  因此在解析电路创建设备的时候,还会加一个判断,看此设备是否是互斥开关,若是,则更新该电路中的branchflags:

if(tempdevice instanceof Device_H){
    int pinNum =getintid(parts_T[j]);//获取互斥开关的 引脚  
    if(pinNum==2)
        majorCircuit.addBranchflag(id_dev,2);
    else if(pinNum==3)
        majorCircuit.addBranchflag(id_dev,3);
}

5.设备状态更新:

  parseK, parseF, parseL, parseH 方法分别解析开关、分档调速器、连续调速器和互斥开关的控制指令,并更新相应设备的状态。

6.电路计算:

  calculateResistance 方法遍历所有电路,调用它们的 calPartCircuitResistance 方法来计算电路的电阻。对于串联电路,还会调用 calElectricity 方法来计算电流。calculateVoltage 方法计算整个电路的电压差,它首先计算主电路的电压差,然后是并联电路中的每个支路的电压差。

  这里也是要注意,在遍历设备计算电路属性的时候,要注意判断是否是互斥开关,以及互斥开关所在引脚,因为不同引脚下互斥开关的电阻是不一样的。

7.结果输出:

  printResult 方法负责输出所有设备的状态。它首先调用 calculateResistance 和 calculateVoltage 来确保所有电路计算都是最新的,然后遍历所有设备,调用它们的 getDeviceState 方法来获取状态,并打印出来。

  注意,由于受控窗帘的输出和整个电路中的光照强度有关,因此在遍历设备计算其状态的时候,要顺便记录一下白炽灯和日光灯的光照强度,将其记录下来。

复制代码
public void printResult() {
    //省略......
// 记录光照强度
    double allLight = 0;
    // 输出设备状态
    for (CircuitDevice device : allSortedDevices) {
        String output = "@" + device.id + ":";
        //省略......
     else if (device instanceof Device_B) { // 白炽灯输出 Device_B deviceB = (Device_B) device; output += (int) deviceB.getDeviceState(); allLight += (double) deviceB.getDeviceState(); } else if (device instanceof Device_R) { // 日光灯输出 Device_R deviceR = (Device_R) device; output += (int) deviceR.getDeviceState(); allLight += (double) deviceR.getDeviceState(); } else if (device instanceof Device_S) { // 受控窗帘输出 和电路中 光照强度有关 Device_S deviceS = (Device_S) device; output += (int) deviceS.getDeviceState(allLight); } //省略...... } }
复制代码

8.程序结束:

  所有设备状态打印完毕后,程序结束。

第八次作业 家居强电电路模拟程序-4

设计类图如下:

在这个类图中:

  • Main 类:程序的入口点,负责创建 TestParser 对象并调用其方法来解析输入并打印电路设备的状态。
  • TestParser 类:负责解析输入的电路信息,包括设备连接、控制指令等,并管理整个电路的模拟过程,包括计算电阻、电压和输出设备状态。
  • Circuit 类(抽象):作为电路的基础类,定义了电路的基本属性和方法,如设备列表、连接信息、状态标志等。提供了创建设备、添加设备、添加连接信息等方法。
  • CircuitDevice 类(抽象):所有电路设备的基类,定义了设备的基本属性,如ID、输入电压、输出电压、电阻、电压差和电流。提供了计算输出电压的方法。
  • ControlDevice 类(抽象):继承自 CircuitDevice,代表控制设备,如开关和调速器。提供了获取输出电压的方法。
  • ControlledDevice 类(抽象):继承自 CircuitDevice,代表受控设备,如灯具、风扇和窗帘。定义了获取设备状态的抽象方法。
  • Device_K 、Device_H 、Device_F 、Device_L 类:分別代表不同类型的控制设备,继承自 ControlDevice。
  • Device_B, Device_R, Device_D, Device_A, Device_S 类:分别代表不同类型的受控设备,继承自 ControlledDevice 。
  • MajorCircuit 类:继承自 Circuit,代表主电路。提供了计算电路电阻和电压的方法。
  • ParallelCircuit 类:继承自 Circuit,代表并联电路。提供了添加连接信息和计算并联电路电阻的方法。
  • SeriesCircuit 类:继承自 Circuit,代表串联电路。提供了计算串联电路电阻和电流的方法。

主要逻辑和详细设计:

  整体框架和上一次大作业差不多,不同的是,考虑到并联中包含并联的情况,这里改用了递归的方法去计算电路的各个属性。

1.初始化:

  在 Main 类的 main 方法中,创建 TestParser 对象,准备开始解析过程。

2.解析输入:

  TestParser 的 parseInput 方法读取用户输入,存储在不同的 StringBuilder 对象中,以区分不同类型的电路信息(如串联电路T,和并联电路M)。

3.创建电路对象:

  TestParser 的 runParsing 方法中,调用 parseConnections_T 和 parseConnections_M 分别解析串联和并联电路的连接信息,创建 SeriesCircuit 和 ParallelCircuit 对象,并为它们分配设备和连接。

4.设备创建与管理:

  Circuit 类中的 createDevice 方法根据输入的设备ID创建具体的设备对象(如 Device_K, Device_F 等),并通过 addDevice 方法将它们添加到电路中。

  因为要输出设备引脚电压状态,而且还可能出现电路中#T1:[IN P2-2] [P2-1 H1-2] [H1-1 OUT]s设备不是按照1-2顺序,而是2-1反着来的,为了处理这种情况,我在CircuitDevice电路设备类中增加了一个protected boolean direction=true;用来标识设备引脚位置状态。

  在创建设备的时候就会调用handleDevice_Direction判断其引脚方向:

复制代码
// 处理互斥开关的引脚 并标识方向Direction,初始化为true,反向则改为false
private void handleDevice_Direction(CircuitDevice device, String part) {
    if(device!=null) {
        int pinNum = getintid(part); // 获取互斥开关的 引脚  
        if (pinNum == 1 && device.isChanged == false) {
            device.direction = true;
            device.isChanged = true;
        } else if (pinNum == 2 && device.isChanged == false) {
            device.direction = false;
            device.isChanged = true;
        }
    }
}
复制代码

5.状态更新:

  TestParser 中的 parseK, parseF, parseL, 和 parseH 方法根据用户输入更新控制设备(如开关K,分档调速器F,连续调速器L,互斥开关H)的状态。

6.计算电阻:

  因为并联电路里面会包含并联电路,所以这里我改用递归来计算电路电阻。通过calEveryResistance方法遍历电路中的所有元素(包括设备和子电路),并调用calR方法来递归地计算每个元素的电阻。对于串联电路,电阻被直接累加;而对于并联电路,电阻则通过并联电阻公式(各电阻的倒数之和的倒数)来计算。

  在这个过程中,每个电路的电阻都会被赋值,并且只有电路是通路的情况下才会计算电阻。

public double calEveryResistance() {
    double totalResistance=0;
    for (String id : connections) {
        totalResistance += calR(id);
    }//计算整个电路的总电阻
    TOTAL_RESISTANCE=totalResistance;
    return totalResistance;
}
复制代码
public double calR(String id) {
    if(id.equals("VCC")||id.equals("GND")||id.equals("IN")||id.equals("OUT")){ return 0;}
    if (isDevice(id))//设备
    {
        CircuitDevice device = allDevices.get(id);
        if(device!=null) {return device.resistance; }
        return 0;
    }
    else if (id.startsWith("T"))// 串联电路
    {
        double totalResistance = 0;
        SeriesCircuit tempseriesCircuit = findSeriesCircuitById(id);
        if(tempseriesCircuit.flag!=2){ return 0;  }
        for (String componentId : tempseriesCircuit.connections) {
            totalResistance += calR(componentId);
        }
        //把电阻值 赋值给 该电路
        tempseriesCircuit.resistance=totalResistance;
        return totalResistance;
    }
    else if (id.startsWith("M"))// 并联电路
    {
        double totalResistance = 0;
        ParallelCircuit tempparallelCircuit = findParallelCircuitById(id);
        if(tempparallelCircuit!=null&&tempparallelCircuit.flag!=2){ return 0; }
        for (SeriesCircuit componentId : tempparallelCircuit.connections) {
            double resistance = calR(componentId.id);
            if(resistance!=0)
                totalResistance += 1.0 / resistance;
        }
        //把电阻值 赋值给 该电路
        if(totalResistance!=0) {
            tempparallelCircuit.resistance = 1.0 / totalResistance;
            return 1.0 / totalResistance;
        }
        return 0;
    }
    return 0;
}
复制代码

7.计算电压差:

  这一部分,也是通过递归方法来计算的,就是有点复杂。

  在各电路电阻已知的前提下,根据上面递归的思路遍历主路的connections计算其中各部分的电压值并将其赋值给对应电路。每遍历一部分,计算出电压差,就会更新输入输出电压,断路、通路都要更新,不考虑短路(因为短路不用输出设备信息),还要重点考虑互斥开关所在的电路。

  这个过程从主电路的输入电压开始,通过遍历电路中的每个连接点(无论是单个设备还是子电路),递归地调用calU方法来处理。对于每个连接点,如果是设备,则根据其类型(控制设备或受控设备)更新电压和电流;如果是子电路,则进一步递归地处理子电路中的组件。

8.输出结果:

  printResult 方法首先调用 calculateResistance 和 calculateVoltage 确保所有计算都是最新的,然后输出所有设备的状态。这包括设备的开关状态、亮度、转速等。

三、踩坑心得

对源码的提交过程中出现的问题及心得进行总结,务必做到详实,拿数据、源码及测试结果说话,切忌假大空

针对第七次大作业:

  一开始,没有考虑到一个电路上可以有多个互斥开关,就简单的给每个电路设置了一个标志位private int branchflag= 0,有一部分测试点没有通过,后来调整了一下用protected Map<String, Integer> branchflagsai存储电路中互斥开关的引脚状态。

  然后是关于受控窗帘的,默认全开是100。

  我一开始写成了1,改一下就好了。

  还有这个,找了好久没找出来的错误,最后是翻看之前学长学姐的博客才知道的:

  

对于同一种设备,是按照字典序进行排列的!!!!!!!!

 针对第八次大作业:

  在判断电路是短路、断路还是通路的时候不考虑全面就容易出错,makeMajorCircuitFlag方法通过初始化断路和短路标志,遍历主电路中的所有连接,跳过电源连接,针对串联和并联电路检查其状态,最后根据是否存在断路或短路以及总电阻是否为0来更新主电路标志,确保准确模拟电路的整体状态。

   由于我是先计算电阻,然后计算各部分电流,通过电阻X电流来计算电压差,这其中难免会出现误差和精度问题,于是代码首先检查输入电压inputVoltage和输出电压outputVoltage是否已经非常接近,即它们之间的差异是否小于1e-4。如果是这样,代码会将这些浮点数四舍五入到最接近的整数,确保在后续的电压差计算中使用精确的整数值。                             还有就是计算电压差的时候,要考虑清楚各种情况。有可能后一个引脚的电压大于前一个引脚电压。

四、改进建议

对相应题目的编码改进给出自己的见解,做到可持续改进

  有的类实现的功能太多了,维护和修改起来不是很方便,下次可以在实现多个功能的类里面设计内部类。

  一个函数里面包含的代码量太大了,尤其是第八次大作业递归那里,各种情况都堆到了一起。

五、总结

对本阶段题目集的综合性总结,学到了什么,哪些地方需要进一步学习及研究。

写一下本学期的收获

  我最后一次大作业的框架其实是挺乱的,因为之前都没有涉及到什么引脚电压输出和并联中包含并联的情况,所以修改起来相当麻烦,只能在原有的基础上硬着头皮改,主要就是更改了之前计算电阻、电压等属性的方法,因为一层套一层的原因,我选择了使用递归去计算,尤其是计算电压差和引脚电压最复杂和麻烦,要分很多种情况,有无互斥开关,设备引脚方向顺序,设备所在电路是什么情况(断路、短路还是通路?),每一种情况下计算电压差方式还不太一样,就更让人头大啦!

  不过在动手敲代码之前,先在纸上写写画画还是会对整体有一个更清晰的认识和帮助的。

  虽然题目很复杂,让人望而生畏,但是静下来去看还是挺有意思的哈。

  这学期的Java课程结束了,还是收获了不少的,经过这八次大作业和实验的“摧残”,编程能力还是提升了不少,而且也对Java有了一个较为深刻的认识,虽然理论知识可能没那么扎实,因为老师让我们课后看网课来学习,但是我我我没有花费很多时间在上面(平时各种各样的课程作业实验已经让我很头大了),所以.......

  不过在编程中,遇到不会的顺手也就去查了,也是学到了很多知识!

 

后会有期!!!!!!!!!!

 

posted @   在凤凰岭吃粉丝的猫  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示