第7-8次作业总结
一、前言
1.家居强电电路模拟程序-3
作为本次题目的第三次迭代,难度相比第二次再次提升了一个档次,加入了互斥开关以及窗帘两种设备,并且会存在多个并联电路。由于并没有出现并联中包含并联,因此难度并没有上升太多。但本次出现的互斥开关的处理着实麻烦,一个设备出现在两个支路中,以之前的处理方法无法正确处理,需要特殊判断。在此次题目中,题目还存在一个错误,排序有误,后续再详解。
2.家居强电电路模拟程序-4
最后一次迭代可以看出难度提升非常大,出现了并联包含并联的情况,这就导致了使用工厂模式的递归深度会非常深,影响程序的性能。此次只添加了二极管设备,二极管的处理还算简单,需要为其添加一个“方向“的属性。此次迭代中,输出也发生了改变,需要输出各个设备的引脚电位以及电流的过载信息,此次迭代最为难的部分就是引脚电位的计算,由于断路的存在,因此电流的流向不完全是完全一致的方向,可能会出现”回流“的现象,以此在断路时,不能简单的判断远离电源正方向的一端的电位为0。
二、设计与分析
1.家居强电电路模拟程序-3
第三次迭代难度并没有上升太多,但互斥开关的加入让电路变得更加复杂,会出现两条支路连接同一个设备的情况,因此需要对其好好设计。
首先,互斥开关具有三个引脚,其中一号引脚接入干路,二、三号引脚接入两条支路,因此在输入时一个互斥开关的引脚信息会输入3次(其实是四次)。在之前的程序中,对于一个设备在输入时,只会处理第一次的输入并将其加入的设备存储类中,后续的输入在设备存储类中查找到后直接跳过不进行处理。但互斥开关并不能延续这种操作,因此需要特殊处理,一个互斥开关其实可以看作为两个开关分别处在两条支路当中,只不过这两个开关的开断是互斥的,但需要记住两条支路中存储的分别是哪一个开关(也就是互斥开关的引脚)。在输入时,一号引脚其实是没有用的,二、三号引脚才具有标识作用,所以一号引脚直接忽略,对于二三号引脚将设备编号以及引脚号一并加入到支路的存储链表中。
互斥开关输入时特殊处理
while (matcher.find()) {
DeviceFLAG = matcher.group(1);
if (Bank.get(DeviceFLAG) == null)
register(DeviceFLAG);
if (DeviceFLAG.contains("H")) { //判断是否为互斥开关
if (!matcher.group(2).equals("1")) //判断是否为引脚1
c.add(matcher.group(0)); //不是引脚1则加入到存储链表中
continue;
}
c.add(DeviceFLAG); //加入到存储类中
}
第二,对于互斥开关的通断的分析设计,互斥开关其实只有两种状态,1、3接通以及1、2接通,这时候在输入时存储的引脚就有作用了。对于1、3接通时,互斥开关状态state = 1,1、2接通时,互斥开关状态state = 0,因此可以得出一个结论,引脚标号减去状态state 等于2的话,则接通,这样就没有那么多if else的判断了,通过一个简单的减法即可得出连通性。
互斥开关的连通性判断
@Override
public boolean getConnectivity(int pinNum) {
return pinNum - state == 2;
}
解决完上述两个问题后,互斥开关的处理其实就以及大同小异了,电阻同样也是根据引脚来判断,电压不用关引脚,直接根据电阻分压即可得出,这里就不多赘述了。
接下来来介绍一下此次迭代的另一个设备——受控窗帘。
对于受控窗帘来说,影响其参数的不只有电压,还有所有灯的亮度。因此计算受控窗帘信息时首先需要计算出所有灯的亮度,再去计算受控窗帘,因此我再计算设备参数方法中的最后单独添加了计算受控窗帘的函数,由于在bank2中同样也存储着受控窗帘,因此在计算出所有灯的亮度前也会计算到受控窗帘,但无伤大雅,最后的calculateWindows()方法会将其先前计算的参数覆盖,最后得出正确答案。
static void calculateWorkDevice() {
for (Device d : Bank.bank2) {
String flag = d.flag;
d.calculateMessage();
}
calculateWindows(); //计算受控窗帘信息
}
类图
2.家居强电电路模拟程序-4
第四次迭代难度大了很多,电路变得更加复杂,出现了并联嵌套并联的情况。其实对于并联嵌套的情况来说,之前的程序利用工厂模式可以完美适配这种情况,稍加完善即可。
其实此次迭代,最难的莫过于引脚电位的计算。若是简单不存在断路的电路,从电源开始顺着电路计算出电压,根据分压就可以计算出两个引脚的电位,但显然这道题并没有这么简单。因此需要从最基本的电路原理入手,对于受控设备来说,两侧的引脚电位因为内部电阻的作用下产生压降从而导致电位不同,两者相减即为自身电压,而两个直接相连的设备的输入输出引脚的电位相同。
因此可以得出整个计算电位的流程,对于串联电路来说,从电路的第一个设备开始,输入为220v,减去自身电压即为输出引脚的电位,紧接着计算相连的下一个设备,该设备输入即为上一个设备的输出。
串联电路电位计算函数
public void calculatePinVol(double InputVol) {
pin1 = InputVol; //串联电路pin1为输入电位
for (String str : devices) { //遍历串联电路
Circuit c = Bank.get(str); //取出该设备
c.calculatePinVol(InputVol); //调用该设备的电位计算函数
InputVol = c.pin2; //重新给输入电位赋值
}
pin2 = InputVol; //串联电路的输出电位
}
而对于并联电路来说,各条支路的输入输出电位都相同,只需循环调用每条支路的电位计算函数即可。
并联电路电位计算函数
public void calculatePinVol(double InputVol) {
pin1 = InputVol;
for (String str : devices) {
Circuit c = Bank.get(str);
c.calculatePinVol(InputVol);
}
pin2 = InputVol - getVol();
}
对于整道题的求解过程,其实还是利用了工厂模式,反复递归求解,但需要整理好整个求解思路,我的求解顺序如下:
-
计算连通性,这里主要是为了解决断路的问题。
-
计算电阻,这里主要是为后续的分压奠定基础。
-
计算电压,有了电阻之后,只需要根据总电阻以及自身电阻,即可计算出自身电压,对于短路的情况,这里也可以当作正常情况处理了,因为在上一步的计算电阻中,已经将该并联电路的电阻计算为0了,按照分压公式来算,其电压为0。
-
计算电流,计算出电压电阻后,电流迎刃而解,根据各个设备的过载阈值输出相应的信息即可。
-
计算引脚电位,如上述一样,根据输入输出引脚的电位传递计算。
-
计算各受控设备参数,根据电压或其他信息计算参数。
由于各类计算方法已经封装好了,只需要调用即可,因此主函数十分简洁,只有各个计算方法的调用语句。
主函数
public class Main {
public static void main(String[] args) {
InputMessage.getMessage(); //输入
Calculate.calculateAllConnectivity(); //计算连通性
Calculate.calculateAllResistance(); //计算电路电阻
Calculate.calculateAllVol(); //计算电压
Calculate.calculateAllCurrent(); //计算电流
Calculate.calculateAllPinVol(); //计算引脚电位
Calculate.calculateWorkDevice(); //计算受控设备的信息
OutputMessage.showAll(); //输出
}
}
类图
三、踩坑心得
家居强电电路模拟程序-3
承接上文,这道题的问题在于排序,谁家好人编号排序按字典序排啊,这D11怎么能排在D2前面呢,什么破题🤬🤬🤬🤬🤬🤬,老师你要是能看到就给改了吧,虽然应该还是助教看到的。
测试用例
正确输出
你看看这合理吗?河里吗?河狸吗?
家居强电电路模拟程序-4
在断路的支路中,右侧的电位不一定为0。可以设想一个场景,两条串联电路并联,其中一条断路,另一条连通,而断路位置左侧的电位即为串联电路的输入电位,而右侧的电位一个为另一条串联电路的输出电位,因为此时另一条串联电路与右侧部分电路时连通的,另一条串联电路电流仍然会向右侧部分电路流通,并且此时右侧部分电路上的各个设备的引脚都相等,大小与另一条串联电路的输出电位相等。
至于这个问题的解决办法,其实可以通过两次遍历解决,分别从首尾去遍历,首部遍历的话,初始电压为220,碰到设备,输入电位减去电压即为输出电位,尾部遍历的话,初始电压为0,碰到设备,输出电位加上电压即为输入电位。这样的话无论是断路左侧还是右侧就都可以计算出来了。
四、改进建议
对于家居强电电路模拟程序来说,采用工厂模式能够很轻松的解决问题,但增加了代码的复杂度,工厂模式采用的是递归来进行遍历的,当数据量太大的时候,递归深度过深可能导致性能问题,并且我的计算类中大都都是采用递归进行计算的,这样就导致程序的性能在面对大数据量时可能会出现问题。
这两次迭代中的特殊判断太多,代码的可读性太差了,像第三次迭代已经是大概一个月前写的了,内部细节已经无法记清,现在回过头来看代码,发现是一坨屎山,各种if else,而且注释也没有。。。因此,需要对程序中的方法设计一个更通用、更健壮的写法,介绍if else的特殊判断,并且添加注释,免得自己都看不到自己写的。
五、总结
在两次迭代的设计与实现过程中,我深刻认识到了编程中逻辑思维的重要性,我逐渐掌握了如何通过面向对象的设计方法解决复杂的实际问题,同时也对递归算法、工厂模式等设计模式有了更深入的理解。
在程序设计中,我不仅体验到了简洁、优雅的代码逻辑带来的满足感,同时也感受到了代码冗杂时的痛苦。在第三次迭代中,互斥开关的处理让我初次感受到针对特殊情况进行设计的复杂性,而在第四次迭代中,并联嵌套的电路问题则让我对递归算法的深度与性能有了更直观的体会。
尽管最终的程序功能实现了要求,但回过头来看,代码的结构和可读性还有许多改进的空间。在日后的编程实践中,我会更加注重代码的健壮性与可维护性,减少硬编码与特殊处理的逻辑,同时加强注释的编写,使代码对自己和他人都更为友好。
这次任务不仅提高了我的编程能力,还让我意识到软件工程中规范和团队协作的重要性。我期待在未来的学习与实践中,能够进一步提升自己的专业技能。
六、学期总结
这个学期的Java学习,我首次接触了面向对象的编程语言。虽然以往自学过C++的语法以及STL,但从未系统地设计过一个完整的工程。本学期的Java课程让我有了更多理论与实践结合的机会,尤其是通过PTA平台的训练和实验项目的完成,我逐渐掌握了面向对象的设计思想,并且尝试应用到实际的问题解决中。
在PTA的训练中,我从最基础的语法练习开始,例如类与对象的使用、继承与多态的实现,到后来的综合性题目,比如设计模拟系统和解决复杂问题。这些练习帮助我巩固了基础知识,也让我感受到面向对象语言的灵活性与强大。
本学期最具挑战性的部分是实验项目,特别是家居强电电路模拟程序的几次迭代。每次迭代的任务难度逐步增加,从简单的串联、并联电路到并联嵌套电路的处理,让我经历了从设计到实现再到优化的全过程。在这个过程中,我不仅学习了工厂模式的使用,还学会了如何通过递归算法解决复杂的结构问题。例如,通过递归遍历嵌套电路,计算电压、电阻等参数,进一步深化了对算法和数据结构的理解。
此外,我还体会到了良好的代码设计对程序维护的重要性。在实验过程中,我遇到过许多“屎山”代码的困扰,因为早期未能充分考虑代码的可读性和扩展性,导致后续调试和改动十分痛苦。这让我意识到注重设计的规范性、减少硬编码和添加清晰注释的重要性。
总的来说,这个学期的Java学习让我受益匪浅。从最基础的语法和类的设计,到复杂系统的工程化实现,我的编程能力得到了显著提升。同时,我也意识到自己在代码优化、性能调优以及大规模项目管理等方面还有很大的改进空间。未来,我希望通过更多项目实践不断提升自己的技术水平,同时深入学习设计模式和算法,为之后的学习和职业发展打下更扎实的基础。