前言
在过去的几周内,我们完成了家居强电电路模拟程序-3以及 家居强电电路模拟程序-4的练习,涉及多个知识点和编程技巧。整体来说,这两次题目集共包含了2道题目,题目难度逐步增加。(折叠代码是不在行列计算内的,只是我觉得结合代码能讲的更清楚我的思路,由于上次可能因为这个原因有些同学打分较低,博主在此作出声明)
题量:
家居强电电路模拟程序-3(1道题), 家居强电电路模拟程序-4(1道题)。
难度:
家居强电电路模拟程序-3的难度相较于前两次大作业并没有显著增加,但它涉及到的知识点更加丰富,尤其是在面向对象编程中的继承与多态的应用。通过设计具备继承关系的类结构,我们能够更好地模拟电器元器件的功能,并且使程序具有更高的扩展性和灵活性。这一过程加深了我们对面向对象编程的理解,同时也要求我们实现更多的电器元器件功能逻辑,增加了代码的复杂性。
家居强电电路模拟程序-4的难度大幅提升。这次大作业引入了二极管这一新的电器元件,要求我们对电工电子技术的知识有更深入的掌握。除了模拟二极管的基本功能,还要求我们输出每个元器件引脚的电压,并判断电流是否过载以及是否存在短路情况。这些要求使得程序设计变得更加复杂,综合考虑了电路的安全性和稳定性。整体而言,程序-4的难度大幅上升,不仅要求编程能力的提升,还需要对电路原理有更深入的理解与应用。
设计与分析
家居强电电路模拟程序-3
设计
思路
本次大作业相较于上次大作业迭代了两种电器元件——互斥开关,受控窗帘。互斥开关同时可以存在于两条串联电路中,实现选择电路接入总电路中,但由于互斥开关并没有要输出对应的引脚电压,所以我们只需关注拥有引脚2的电路互斥开关的状态即可,当拥有引脚2的互斥开关,状态为flase时,对应状态为closed,则拥有引脚3的电路中的互斥开关则为ture,对应状态为turned on。在后续输出互斥开关状态时,我们只需将拥有引脚2的互斥开关加入输出,引脚3的互斥开关不加入输出。受控窗帘要根据灯光元器件的亮度更新自己的状态,所以我们要定义一个变量brightness去记录电路中灯光元器件的亮度,最后在更新窗帘状态。
类图
家居强电电路模拟程序-3顺序图
分析
报表数据
从报表数据可以得知,除 Avg Depth,Max Complexity偏高外其它各项基本正常。
下面是部分代码的截取
class HSwitch extends Control {private boolean state; // 开关的状态,true表示1接2,false表示1接3int out ;double resistance;public HSwitch(String id, int out, double resistance) {super("H", id, resistance, "closed");//System.out.println(out+" "+resistance);this.state = true; // 默认状态是1接2this.out = out;}public double getResistance(int out) {if (out == 2) {return 5; // 1接2时电阻为5} else {return 10; // 1接3时电阻为10 }}@Overridepublic String getStatus() {return state ? "closed" : "turned on"; // 返回开关状态 }public void toggle() {this.state = !this.state; // 切换状态// 如果state为true,连接1到2;否则,连接1到3this.status = state ? "closed" : "turned on"; // 更新状态描述 } }
HSwitch
类是一个用于表示互斥开关的控制元件,继承自 Control
类。该类的设计旨在模拟电路中互斥开关的行为,允许用户在两条电路之间进行选择。类中包含几个重要的成员变量:state
表示开关的状态(true
表示连接端口1到端口2,false
表示连接端口1到端口3),out
表示输出端口,resistance
表示电阻值。构造函数 HSwitch(String id, int out, double resistance)
初始化开关的 ID、输出端口和电阻,默认状态设为 true
。getResistance(int out)
方法根据当前连接的输出端口返回相应的电阻值(端口2时为5,端口3时为10)。getStatus()
方法返回开关的当前状态描述,便于查询。toggle()
方法用于切换开关状态,并更新状态。
class Curtain extends Controlled {private int openPercentage;private int lx;// 窗帘开合百分比,0表示全关,100表示全开public Curtain(String id) {super("S", id, 15); // 窗帘的电阻为15this.openPercentage = 100; // 默认全开 }@Overridepublic void updateState(double inputVoltage) {// 如果电压低于50V,窗帘不工作if (inputVoltage < 50) {this.openPercentage = 100; // 电压低于50V时,默认为全开状态} else {// 电压达到50V及以上,开始根据光照强度控制窗帘// 假设光照强度已经通过某种方式获取到,这里通过一个假设的参数传入double lightIntensity = getLightIntensity(); // 获取光照强度 (lux)// 根据光照强度调整窗帘开合比例if (lightIntensity < 50) {this.openPercentage = 100; // 全开} else if (lightIntensity < 100) {this.openPercentage = 80; // 80%开} else if (lightIntensity < 200) {this.openPercentage = 60; // 60%开} else if (lightIntensity < 300) {this.openPercentage = 40; // 40%开} else if (lightIntensity < 400) {this.openPercentage = 20; // 20%开} else {this.openPercentage = 0; // 全关 }}}// 获取窗帘开合状态的字符串表示 @Overridepublic String getParameter() {return this.openPercentage + "%"; // 返回窗帘开合的百分比 }public void setLx(int lx) {this.lx = lx;}public double getLightIntensity() {return lx;} }
Curtain
类是一个用于模拟受控窗帘的控制元件,继承自 Controlled
类。该类的设计目的是根据光照强度和输入电压来动态调整窗帘的开合状态。窗帘的开合百分比通过 openPercentage
变量表示,范围从0%(全关)到100%(全开),默认状态设为100%(全开)。构造函数 Curtain(String id)
初始化窗帘的 ID,并设置电阻值为15。updateState(double inputVoltage)
方法用于根据输入电压和光照强度来更新窗帘的开合比例。当输入电压低于50V时,窗帘保持全开状态;当电压达到50V及以上时,窗帘根据光照强度进行调整,使用 getLightIntensity()
方法获取当前光照强度,并根据预设的光照阈值调整开合比例。getParameter()
方法返回窗帘当前的开合状态(百分比),setLx(int lx)
和 getLightIntensity()
方法用于设置和获取光照强度。
其他类与上次大作业类似,这里就不一一描述。
家居强电电路模拟程序-4
设计
思路
最后一次大作业迭代了一个元器件——二极管,输出每个引脚的电压,以及判断电流在元器件内是否过载,电路是否短路。首先二极管是正向电阻趋近于0,反向电阻无穷大。所以我们设计一个引脚数组来记录我们所连接的设备,每个元器件的输入等于上个元器件的输出,所以我们要记录每个元器件的下一元器件是什么,然后后面更新其输入引脚的电压,通过元器件的分压,我们可以用输入减去分压得到输出引脚的电压,分压用来更新我们的设备状态。由于要判断每个元器件是否过载,我们要在device类中定义判断过载的方法,在电路中定义判断短路的方法,即主电路中没有电阻则说明出现了短路。
类图
家居强电电路模拟程序-4顺序图
分析
报表数据
从报表数据可以得知,除 Avg Depth,Max Complexity偏高外其它各项基本正常。
下面是部分代码的截取
/*其他与上次大作业相同*/ private Device nextDevice; protected boolean check=true;protected double[] pinsVoltage=new double[4]; public void toggleCheck(double current1){if (current<current1)check=false;}public void checkCurrent(){if(!check)System.out.print(" exceeding current limit error");}public void setPinsVoltage(double voltage,double pinID) {this.pinsVoltage[(int)pinID]=voltage;}public double getPinsVoltage(double pinID) {return this.pinsVoltage[(int)pinID];}public Device getNextDevice() {return nextDevice;}// 设置下一个 Devicepublic void setNextDevice(Device nextDevice) {this.nextDevice = nextDevice;} }
这个方法是用一个boolen类型check来判断通过元器件的电流是否过载,过载则为false。this.pinsVoltage[(int) pinID]
表示将 pinID
对应的引脚的电压值设置为 voltage,
pinsVoltage
是一个数组,其中存储了每个引脚的电压值。强制类型转换 (int)
是为了确保引脚ID适应数组的索引要求。
public void setConnect(SeriesCircuit seriesCircuit){for (Device device:seriesCircuit.devices){Device device1=device.getNextDevice();if (device1==null)return;if (device1 instanceof MCircuit){MCircuit mCircuit=(MCircuit)device1;for (SeriesCircuit seriesCircuit1:mCircuit.seriesCircuits) {for (int i = 0;i<1; i++) {Device device2 = seriesCircuit1.devices.get(i);device.setConnects(device2.getNextDevice(),device2.getNextDevice().input);}}} else if (device1 instanceof SeriesCircuit) {SeriesCircuit seriesCircuit1=(SeriesCircuit) device1;for (int i = 0;i<1; i++) {Device device2 = seriesCircuit1.devices.get(i);device.setConnects(device2.getNextDevice(),device2.getNextDevice().input);}}elsedevice.setConnects(device1,device1.input);}}
该方法 setConnect
的设计思路是通过递归连接电路中的设备,确保元器件按照串联或并联方式正确连接。在遍历 SeriesCircuit
中的每个设备时,首先检查设备的下一个连接对象 device1
。如果 device1
是一个复合电路(如 MCircuit
),则递归地遍历其内部的子电路,并将当前设备连接到子电路中的第一个设备。若 device1
是一个 SeriesCircuit
,则同样连接到其第一个设备。对于其他类型的设备,直接进行连接。该设计实现了设备间的自动连接和层次化处理。
public void setVoltage(Device device, double voltage,double d) {if (device == null) {return;}// 设置输入端的电压 device.setPinsVoltage(voltage, device.input);// 计算输出电压并设置double outVoltage = device.getPinsVoltage(device.input) - d * device.resistance;device.setPinsVoltage(outVoltage, device.output);// 设置每个连接的设备的电压for (Device device1 : device.devices) {device1.setPinsVoltage(outVoltage, device1.input);}}
这个方法的设计思路是通过设置和计算电压来模拟电路中元器件的电压变化。首先,方法检查输入的device
对象是否为null
,避免空指针异常。接着,方法将输入电压voltage
设置到设备的输入引脚。然后,根据设备的电阻值和已知电流值和已知的输入电压,计算出输出电压outVoltage
,并将其设置到设备的输出引脚。最后,方法通过遍历与该设备连接的其他设备,将计算得到的输出电压传递给它们的输入引脚。此设计确保了电压在电路中的传递和更新,同时考虑了设备间的电压依赖关系。
class Diodes extends Control{private boolean state;public Diodes(String id,double input,double output){super("P", id,0, "turned on",8,input,output);this.state=false;}@Overridepublic String getStatus() {return state ?"conduction":"cutoff";}public void setState(double input,double output){state=getState(input,output);}public boolean getState(double input,double output){double voltage1=getPinsVoltage((int)input);double voltage2=getPinsVoltage((int)output);if(voltage2<voltage1)return true;else if (voltage2==voltage1){if(input==1){return true;}else return false;}return false;} }
该类通过 state
变量表示二极管的状态,true
表示导通,false
表示截止。构造函数初始化二极管的基本信息,并设置初始状态为关闭(false
)。getStatus
方法根据当前状态返回二极管的工作状态(导通或截止)。setState
方法根据输入和输出电压来更新二极管的状态,利用 getState
方法判断电压差是否满足二极管导通条件。
踩坑心得
这两次作业中坑都比较多,下面我就说说我碰到的几个坑。
坑点1.同一电器设备时输出是按字典序来排序的。
当测试点为
#T1:[IN D2-1] [D2-2 H1-2] [H1-1 OUT]
#T2:[IN D1-1] [D1-2 H1-3] [H1-1 OUT]
#T6:[IN K4-1] [K4-2 OUT]
#M1:[T1 T2 T6]
#T4:[IN B2-1] [B2-2 OUT]
#T5:[IN K1-1] [K1-2 B1-1] [B1-2 OUT]
#M2:[T4 T5]
#T10:[IN K11-1] [K11-2 R2-1] [R2-2 OUT]
#T9:[IN R1-1] [R1-2 OUT]
#M3:[T9 T10]
#T3:[VCC K2-1] [K2-2 M1-IN] [M1-OUT M2-IN] [M2-OUT M3-IN] [M3-OUT GND]
end
输出结果
@K1:closed
@K2:closed
@K4:closed
@K11:closed
@B1:147
@B2:147
@R1:180
@R2:180
@D1:0
@D2:0
@H1:turned on
正确输出结果应为
将Comparator.comparingInt(s -> Integer.parseInt(s.getId())改为Comparator.comparing(SpeedRegulator::getId)后会按照字典序进行排序输出。
坑点2:输入和输出引脚的编号不固定,输入引脚不一定是1,输出引脚不一定是2,而是要看先连接的是1还是2。依据设备首次连接时的引脚设定为 input,之后的引脚则作为output。后面输出时,只要调用input和output记录的值即可解决。
坑点3:当二极管正向接入电路时,其特性决定了电流仅能从阳极流向阴极,因此一端会受到电源电压的影响,另一端通常接地或接零电压。这种情况下,即使二极管的内阻为零,电压依然不会在二极管两端完全相同,因为电流的流动方向受限于二极管的单向导电特性。二极管正向接入时,其阳极电压接近电源电压,而阴极电压接近零,确保电流在合适的方向流动,并通过二极管进行控制,避免电流反向流动导致损坏。后面根据更改部分逻辑后得以解决。
改进建议
1.在当前代码中,引脚数量固定为4个,主要是由于最多需要支持3个引脚的互斥开关。然而,随着功能需求的增加,可以通过动态数组实现引脚数量的灵活管理,从而根据实际需要动态添加或删除引脚。
2.在电路设计中,当存在并联电路时,电路断路可能会导致电压路径发生变化,进而影响输出电压的计算。例如,如果并联电路中的一条电路断开,那么某个元器件的输出引脚的电压不再单纯由输入电压和分压关系决定,而是可能由另一条电路中的元器件输出电压传递过来。这种情况下,电路的工作状态会发生改变,导致电压分布不再按预期进行。这个改进我解决不了,属于没有实力。
总结
在这四次大作业中,我深刻意识到自己在代码设计上的不足,尤其是在规范化封装方面。由于过于注重功能的实现,导致代码的粘合性非常高,复用性较差。虽然每次都为完成任务而尽力编写代码,但在很多细节上并没有做到合理的封装和重构。尤其是在方法和属性的设计上,没有根据模块化、复用性和可维护性的原则进行精心规划,而是将大量时间投入到功能实现的过程中,忽视了代码的结构和可扩展性。这样的做法虽然可以在短期内完成任务,但从长远来看,却降低了代码的质量和可维护性。最终,由于缺乏足够的代码设计思维和规范化管理,我在最后一次大作业中遭遇了很大的困难,导致最终成绩不理想。这个教训让我意识到,编写高质量代码不仅仅是为了实现功能,更要注重代码的结构、可复用性以及长期可维护性。
本学期的收获以及心里想说的话
首先,我要由衷地感谢老师的辛勤付出和悉心指导。在每次大作业中,尽管我有一些测试点未能通过,老师都会耐心地指点我,帮助我发现问题并改进。此外,有一次由于我的疏忽错交了作业,老师也非常理解,特地重新开通了提交通道,给予我补交作业的机会。这种包容与理解让我深感温暖,也让我更坚定了努力学习的决心。
更让我感动的是,老师非常善于倾听我们学生的心声。在很多时候,老师会根据我们的实际情况,适时调整作业的完成时间,给予我们更多的时间去充分理解和完成任务。在这段学习旅程中,老师的耐心与关怀让我感受到了一种无形的支持和鼓励,这使我能够在困境中坚持下来。
然而,尽管得到了如此多的帮助和支持,我依然未能很好地完成最后一次大作业。通过反思,我意识到,在这门课程中,天赋和勤奋缺一不可。光靠勤奋虽然能帮助我弥补一些不足,但仅凭努力却无法弥补思考上的差距。我常常感到自己干什么都慢人一步,导致在这门课程中学习得并不扎实。虽然这让我感到有些遗憾,但也促使我反思自己的学习方法和思维方式,认识到在技术学习中,思维的敏捷性和方法的高效性同样重要。
这篇博客的完成标志着这门课程的落幕,但学习是永无止境的,我将继续追求更远的目标。在这门课程中,我不仅学到了编程的技能,更重要的是,它教会了我如何以更加抽象和模块化的方式来思考问题,如何在复杂多变的环境中保持代码的清晰与简洁。
每一段经历都让我更加明白,学习不仅仅是掌握知识的过程,更是一个不断发现自己不足、调整自己、不断进步的过程。我相信,在未来的道路上,我会不断积累经验,完善自己,挑战更高的目标,走得更远。