一、 前言
1.前言的前言
最近辛苦啦,谢谢你在繁忙的日子来到我的博客!
马上就2025年了,祝你新年一切顺遂呀~~
2.知识点
面向对象编程
需要创建多个类来表示不同的设备和电路类型,例如控制设备类(开关、调速器等)、受控设备类(灯、风扇、窗帘等)、串联电路类和并联电路类。每个类应该封装自己的属性和行为。
设计模式
工厂模式用于创建对象而无需指定具体的类。这可以简化代码,并使它更容易适应变化。使用组合模式,可以灵活地构造任意复杂的电路拓扑结构,并且能够一致地对待单个设备和组合设备。
电路原理
理解基本的电路连接方式(串联和并联等),以及电压、电流、电阻之间的关系。还需要考虑短路检测、电流限制等实际问题。
数据结构
使用到列表或其他集合来存储电路中的设备信息,以及处理输入输出的数据。
算法逻辑
实现计算电压、电流、亮度、转速等功能,处理用户输入的命令,并根据这些命令调整设备状态或参数。
异常处理
对于可能出现的错误情况(如短路、超过最大电流限制),需要有适当的处理机制。
输入输出管理
解析输入格式,按照指定格式输出结果。
字符串处理
用于解析输入命令和生成输出信息。
数学运算
进行各种计算,比如比例计算、条件判断等。
3.题量
需要编写多个类和方法来完成整个系统。除了核心功能外,还需处理边界情况和错误检查,确保程序健壮性。
4.难度
比较难,特别是最后一次大作业,很多情况要考虑。加上期末周,大量作业、课设、考试,大家没有很多勇气和力量去沉下心debug了。
二、设计与分析
1.题目集7的家居强电电路模拟程序3
(1)题目概述
在家居强电电路模拟程序2的基础上新增控制设备互斥开关,新增受控设备窗帘。互斥开关有三个引脚,引脚与引脚之间电阻不同,还可以正反接,受控串联根据电路中光照强度打开程度不一样。线路中包含多个串联起来的并联电路。
(2)类图展示
(3)设计思路
整体来讲,这道题没有很大的跨越。
我之前的代码就可以处理多个串联起来的并联电路,受控串联也是比较容易实现,遍历电路中设备计算光照强度即可。关键是互斥开关的处理。
之前没有特别关心过引脚的问题,对这道题及之后的迭代产生了一些抵触。我想了两种,第一种是看成两个开关,进行一些处理和判断,也将设备加入进来,最后删除。另一种是开关里面有三个电压两个电阻,计算时判断哪个引脚哪个值。但是感觉这两个都不太优雅。最后还是决定采用两个开关的,对前面的代码有所复用。
(4)源码分析
对于受控窗帘,主要是计算亮度的方法,在总电路中遍历所有设备:
public double AllBrightness(){ double brightness=0; for(ElectricalDevice device : deviceALL.values()){ if(device instanceof Light){ brightness+=((Light)device).getParameter(); } } return brightness; }
对于互斥开关,把它看作两个Switch进行处理:
class MutexSwitches extends ControlDevice { boolean InState2 = true; // 闸刀位置在2 Switch switch12; // 反向也可以直接在switch里处理 Switch switch13; //省略一些方法 @Override public void adjustControl(String command) { if (command.startsWith("#" + id)) { InState2 = !InState2; if (switch12 != null) switch12.setR(InState2 ? 5 : Double.POSITIVE_INFINITY); if (switch13 != null) switch13.setR(InState2 ? Double.POSITIVE_INFINITY : 10); } } public String getStateString() { return InState2 ? "closed" : "turned on"; } }
关键函数的顺序图如下:
(5)性能评估
该文件共有847行代码,包含261个语句。分支语句占总语句的10%,代码中存在一定的条件分支逻辑,可能影响代码的可读性和维护性。方法调用语句有79个,代码模块化较好。注释行占总行数的43%,代码有较好的说明,有助于理解和维护。
在类和接口方面,代码定义了10个类或接口,每个类平均包含7.20个方法。每个方法平均包含3.51个语句。最复杂的方法位于第188行,名为MutexSwitches.adjustControl(),其复杂度为6。最深层次的代码块位于第745行,最大嵌套深度为5。整个文件的平均块深度为1.42,平均复杂度为1.72。
尽管代码较长且包含多个类和方法,但整体代码结构较为清晰,复杂度适中。注释比例较高,有助于理解代码逻辑。然而,最复杂的方法和最深层次的代码块需要进一步优化,提高代码的整体质量和可维护性。
2.题目集8的家居强电电路模拟程序4
(1)题目概述
在家居强电电路模拟程序3的基础上,增加了二极管元件,要求显示电压、限制电流、判断短路。可以并联包含并联。
细节上,考虑了各类设备的并联接入,例如,K1 的输出接到 L2 的输入,L2 的输出再接其他设备属于串联接线。K1 的输出接到 L2 的输出,同时 K1 的输入接到 L2 的输入,这种情况属于并联。没有接线的引脚默认接地,电压为0V。本系列题目中元件的管脚除了互斥开关的1引脚,其他所有引脚在电路中最多只出现一次。
(2)类图展示
(3)设计思路
管脚电压我在前面几次已经设计好了,只需要改一下输出。
二极管也类似于开关,就是要处理一下正反接的问题,没有特意处理管脚的类或方法,也没有想到很优雅的算法,所以要写很多冗余的引脚判断。
因为要检测短路和判断过载,所以要有计算电流的方法,我用电压除以电阻的方法。
但是没有接线的引脚默认接地,电压为0V等等细节的情况没考虑得很完善,对特殊情况的处理也不够细致。
(4)源码分析
串联电路计算电流的方法:
public void computeCurrent() { current=differenceVoltage/getR(); if(Double.isInfinite(getR())&&Double.isInfinite(differenceVoltage)){ current=0; } for(ElectricalDevice device : getDevices().values()){ if(device instanceof Circuit){ ((Circuit)device).computeCurrent(); } device.setCurrent(current); } }
并联电路计算电流的方法:
@Override public void computeCurrent() { for (ElectricalDevice device : getDevices().values()){ if(device instanceof Circuit) { ((Circuit)device).computeCurrent(); } } }
防反接机制:
protected double direction=1; // 正向1或反向0 for(ElectricalDevice device : deviceListAll){ if(device.getDirection()==0){ device.checkVoltageDirection(); } }
(5)性能评估
共有 1,183 行代码,其中包含 424 个语句。分支语句占总语句的 15.6%,方法调用语句有 126 个,注释行占总行数的 38.1%。代码中有较多的注释,有助于理解代码逻辑。
项目中共有 11 个类和接口,每个类平均包含约 9 个方法。每个方法的平均语句数为 4.23,方法较为简洁,易于理解和维护。最复杂的代码段出现在第 292 行的 MutexSwitches.adjustControl() 方法中,其复杂度为 6。
最大块深度为 5,平均块深度为 1.36,代码结构相对简单,没有过多的嵌套逻辑或复杂的控制流。整体而言,平均复杂度为 1.78,说明代码整体上较为清晰,没有过多的复杂逻辑。
较高的注释比例(38.1%)表明代码具有较好的可读性和可维护性。较少的分支语句(15.6%)和较高的方法调用语句(126 个)表明代码可能采用了模块化的设计,通过调用多个方法来实现功能,而不是在一个方法中处理所有逻辑。
综上所述,该程序在代码质量和可维护性方面表现良好,具有较高的代码清晰度和较低的复杂度。
三、踩坑心得
1.题目集7的家居强电电路模拟程序3
(1)问题
问题:互斥开关的处理上,看做了两个开关,将这两个开关的设备也加入电路的设备中。
最后输出要进行删除的特别处理。
此时,报了以下错误:
解决:ConcurrentModificationException ,并发修改异常,这是由于一边对ArrayList遍历,一边操作其中的元素造成的。于是,又创建了一个这个ArrayList的副本ArrayList,进行删除,代码如下:
(2)问题
问题:把互斥开关看成两个开关还有一个问题,如果只连接了互斥开关的某两个引脚,又没有特殊处理的。
会有空指针异常。
解决:要在多个地方对两个开关判断非空才进行处理,感觉还是很丑陋的写法。类似下面的代码:
(3)问题
接下来是最后让我改了两天的三个测试点......
问题:多串联电路的一个测试点。
解决:测了许多的串联的样例才解决,是总电路的电压初始化有问题。
(4)问题
问题:32,33没过。
解决:这个是去掉了一个判断条件就可以了。
如图,去掉了注释中的判断条件。但是有没有这个对电阻的判断,在逻辑上似乎是相同的。我猜测是精度问题,一些数学和编程的不等同性,但是不确定。至于下面那个为了提高精度同时乘以100000的操作,是我的一些尝试,并不影响正确性也没有让我改对。
附上一个知识点:
在Java中,当尝试将一个浮点数(float或double)除以0时,结果不会抛出异常。相反,根据IEEE 754标准,这种操作会得到一个特殊值:
如果被除数是正数,那么结果将是正无穷大(+Infinity)。
如果被除数是负数,那么结果将是负无穷大(-Infinity)。
如果被除数是0.0,那么结果将是NaN(Not-a-Number),表示结果是未定义的。
这些特殊情况可以通过Float或Double类中的常量来表示,例如Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, 和 Double.NaN。同时,可以使用Double.isInfinite()方法来检查一个值是否为无穷大,以及用Double.isNaN()来检查一个值是否为NaN。
在Java中,如果尝试将一个整数(int或long)除以0,这会导致运行时异常。具体来说,程序会抛出一个ArithmeticException,其错误信息通常是/ by zero。这是因为对于整数类型,除以零的操作是未定义的,并且被认为是非法操作。
2.题目集8的家居强电电路模拟程序4
(1)问题
问题:输出顺序的问题,因为特殊处理,虽然反接不影响电路特性,但是影响输出的结果。
解决:设置接入反向的标记,输出前特殊处理。
(2)问题
问题:
解决:输出一些数据看一看,哦,原来是无穷除以无穷会NaN啊。
就增加一个判断条件。但是这个好像没有在测试点内,改了分数没变。
同理,我想也该对0除以0的情况判断,比如并联电路一路中只有一个正接的二极管的情况。也增加了判断条件,也确实过了一部分,但是之前的一部分测试点又过不去了,反而分数更低了,花了很长时间,到最后也没想明白应该怎么改。但是也算努力过了。
四、改进建议
(1)降低复杂度的操作
buildInfo方法圈复杂度比较高,对其进行了降低复杂度的操作。
提取子功能:将不同的逻辑部分提取到独立的方法中,这样每个方法只负责一个特定的任务。
减少嵌套层级:通过提前返回来减少代码的嵌套层级。
简化条件判断:合并相似的条件判断,避免重复代码。
(2)避免硬编码值
将常量(如电压值、电阻值)定义为静态最终变量,这样不仅便于维护,还可以通过改变一个地方的值来影响整个系统的行为。
public static final double DEFAULT_INPUT_VOLTAGE = 220;
(3)在构造函数中初始化
MutexSwitches类中的成员变量switch12和switch13可能为空,这可能导致空指针异常。在构造函数中初始化这些成员变量,或者确保它们总是被正确赋值。这样会合适一些。
(4)改进状态管理
对于像Switch和MutexSwitches这样的控制设备,它们的状态变化可能会影响系统的整体行为。可以考虑引入状态模式来更好地管理和追踪这些设备的状态转换。
(5)使用枚举替代字符串标识
对于像引脚名称(如 "VCC" 或 "IN")这样的固定标识符,建议使用枚举类型而不是字符串常量。这不仅能减少拼写错误的风险,还可以提供更好的编译时检查和支持IDE的自动完成功能。
(6) 单一职责的重构
一些类承担了过多的责任,比如电路类,它不仅管理设备和连接,还处理电压计算、电流检查和状态打印。可以考虑将这些功能分解到不同的类或方法中,每个类或方法只负责一个具体的任务。例如,可以给管教分一个类,这样很多逻辑会清晰许多,再例如,创建一个专门用于计算电路参数(如电压和电流)的类。
五、总结分析
这两次大作业让我感到我的类的设计还有很多不合理的地方,代码也不够健壮,不能处理很多特殊情况。这体现了我在这方面的欠缺,不应该只习惯于上手写代码,遇到问题应该先分析和设计,思考解决方案,后续还应该多学习设计模式,多练习,多体会,多总结。
六、学期总结
在这学期的Java课程中,我受益匪浅。除了编程方面的知识,还学习了面向对象的编程思想,我觉得这给我提供一种新的思考方式,它鼓励我以更加结构化、模块化的方式来思考和解决问题。单一职责原则、开放封闭原则、里氏替换原则、迪米特法则等设计原则,不仅是设计与编程的指导,对生活也很有启发,比如,专注于做好一件事,在不破坏已有稳定状态的前提下寻求成长和发展空间,确保替代方案至少能达到原有标准等。我想,这也是理工科的浪漫之处。
调试到深夜只剩电脑微光照在脸上的孤勇,会被绞尽脑汁之后通过测试点的舒畅所抚慰。总是功利地紧张地追求分数,脆弱的心态的不断崩塌又重建,迷失在黑夜里的时候,大致无愧于心的追寻与学习就是意义本身。不管怎么样,过去的好的坏的我都可以去接受。
总结的总结
Java课结束了,这学期的艰巨任务还没有结束,人生一直在路上,一起继续前进吧~
最后,发自内心地感谢在Java以及其他课程的学习中帮助过我的亲爱的老师和同学!
也感谢可爱的你这么认真的读完我的博客!