时间飞逝,又一个学期从指尖下的键盘缝隙里溜走,Java的学习也迎来的这一学期的尾声
那么接下来依然是这个环节------第三轮OOP作业总结
最后这两次的大作业迭代难度非常的大啊
对于这两次大作业,我的整体感受真的是难以言表,对于一条电路的连通包含的所有状态、电流电压等
虽然自己想的时候逻辑比较清晰,但是一转到代码实现感觉好乱
所以说想要学好这Java,光有空想的逻辑只是成功的基础,基于逻辑写出代码的能力亦是重中之重
同时对于不断加深复杂的代码,使用一定的算法也是尤为重要的
仅仅用普通逻辑看起来已经不够解决问题的了

接下来再让我重新介绍一下我的代码:
一、家具程序的类设计及类更迭
---------------------------------------------------------------------------第七次作业迭代类图(多并联电路)

这一次迭代加入了多并联的模式,其实抛开表面上的不谈,这一次的迭代跟上一次的区别其实不大
因为底层逻辑的变化不大
换句话说,他们都是 [一条主路] + n条[分支一次路]
而因此,我的代码可复用性就很高了
首先是上一次类基础上的新加设计:
- Circuit类:新增slaveLine属性,即该条输入的电路从属的电路名的名称,用于区分串联电路与并联电路,二者在电阻电流等的计算方法不一样
- 新增的元器件互斥开关,整合进Switch类内,即视作普通开关
- 新增Curtain类用于容储受控窗帘
其次是电路连接:
- 在完成设备新增与参数配置环节后,首先进行统一的开关检查,对于每一条路的综合开关状态转递至整条路的启用与否
- 根据得来的电路状态计算总电阻与并联电路分流比例
- 以计算串联干路电流、并联各支路电流,再乘以改元器件电阻的方式来计算属于该元器件的电压降(电位差)
- 传递由 V = I * R 得来的电压降,传入用电设备计算转速和流明亮度等
其他方面:
- 使用公式
以代替
来计算并联电路电阻计算,对于相同从属路名一致的路使用此公式,那么就算是两路一断一开,也不会计算出错 -
对于互斥开关,将之视作普通开关存入电路,因为在不同路内的 [2引脚] 和 [3引脚] 开关同名,只需确保初始状态为2开3关,后续的变动遍历该同名开关则可,状态输出时以2引脚开关为准
- 对于受控窗帘的输出需要计算所有灯的亮度和,那么当在输出方法内各个种类的元器件集合
但是这样做,互斥开关的信息不统一
将互斥开关视作普通开关后虽然本身Switch类的修改不大,但是需要另加方法、循环、判断处理互斥开关正反接,开闭合、同名信息的整合
代码中则会使原本的方法内出现好多仅对互斥开关起作用的判断,这无疑对于代码的内部逻辑和圈复杂度,时间复杂度都有一定的冲击
所以说这样做,虽然对于电路逻辑上没有什么问题,但是对于代码的实用性有负面影响
对于下一次的迭代造成了很大的麻烦
---------------------------------------------------------------------------第七次作业迭代类图(多并联电路)

因为最近有好多考试,人间之事纷繁复杂,这次迭代没得满分,而且许多功能存在缺陷
而且底层逻辑也发生了大变化
[一条主路] + n条[分支一次路] + n条[分支二次路]
所以说工具类里的输入方法小改一下,元器件参数、状态、计算方法都要重新改编
但是,当我把电路抽象化一下:
○——⊕——☀——IN↓——○
○——®——⊙——IN↓——○
○——❉——❉——❉——○
在本路里具有指向下一条路的标识,这不就是递归算法吗(虽然我现在才意识到)
所以,原来的方法虽然要重写,但是有了递归思想作指引,那些个方法就大同小异了
首先是上一次类基础上的新加设计:
- 新增二极管Diode类,该类负责单向控制电流的进入及输出
- 所有元器件类新增最大限制电流属性,并在输出时判断验证
- 为解决单刀双掷开关遗留的问题,新增SPTDSwitch类以来存储开关的信息,但是此类仅在输出信息时启用并生效,不参与电路计算
其次是电路连接:
- 根据输入信息,创建元器件类,并且根据引脚顺序标定类内引脚编号,遇到互斥开关则单独判断其状态等
- 处理串联包含串联的情况,将所有非并联电路统合到主路内
- 根据各路里所存在的开关,由主干路开始递归调用方法来正确判断电路启用状态
- 根据得来的电路状态计算电阻
- 以计算串联干路电流、并联各支路电流,再乘以改元器件电阻的方式来计算属于该元器件的电压降(电位差)
- 递归调用方法进行电压的传递,计算电阻乘以电流,再被减去接入引脚的电压,并向下传递
- 从GND口反向出发纠正与之直接相连的输出引脚电压为0
- 计算各个元器件状态及参数
其他方面:
- 调整输出方法,输出状态参数、引脚电压及过载信息
-
互斥开关在最后的输出时把同名开关统合到一起,再进行输出
-
使用递归法,方法分为两部分,第一部分首先对于整条路的开关问题进行判断,第二部分则根据已有并联入口的状态向下寻找并赋值
这个递归方法的使用例如:
public static void connectCurrent(LinkedList<LinkedList> alllist,String SSID,double current) {
// ↘所有路的集合 ↘电路的从属路名 ↘由上一级电路IN传下来的电流
for(int i=0;i<alllist.size();i++) {
LinkedList<circuit> list = alllist.get(i);
if (list.get(0).slaveLine.equals(SSID)) {
// ↘仅对从属路名同名的电路生效
......
}
}
for(int j=0;j<alllist.size();j++){
LinkedList<circuit> list = alllist.get(j);
if (list.get(0).slaveLine.equals(SSID)) {
for(int k=1;k<list.size();k++){
if(list.get(k).getName().equals("IN")){
// ↘寻找下一级并联入口IN
connectCurrent(alllist,list.get(k).SSID,list.get(k).getCurrent());
}
}
}
}
}
当然上述代码是需要把整条路的电流计算完再进行下一次递归,例如电阻计算则遇到IN就递归计算而不是本条路算完
对于这次并联包含并联的迭代,因为我没有完整的写完,所以部分功能存在缺陷,例如互斥开关的三个引脚赋值问题,以及复杂电路的处理问题都未得到妥善解决
二、测试问题的解决
❉串联路里包含子级串联路
这个问题的存在其实好解决,子级路本身就是主路的一部分,那么我们就让他回到主路的连接里去:
VCC—K1—R1—T1-IN—T1-OUT—GND
IN—B1—F1—OUT
转换为:
VCC—K1—R1—B1—F1—GND
1 public static void processingMergeircuits(LinkedList<LinkedList> alllist) {//将应该存在于主干路的附属路转嫁到主路,并删除
2 for (int i = 1; i < alllist.size(); i++) {
3 LinkedList<circuit> list = alllist.get(i);
4 String road=list.get(0).slaveLine;//获得这条路的从属路名,也就是T1、M1等
5 if(road.startsWith("T")){//非并联路
6 LinkedList<circuit> mainRoad = alllist.get(0);
7 for (int j = 0; j < mainRoad.size(); j++) {
8 if(list.get(0).getName().equals(mainRoad.get(j).getName())&&road.equals(mainRoad.get(j).slaveLine)){
9 mainRoad.addAll(j+1, list);//将子级路转嫁到主路上
10 alllist.remove(i);//删除
11 break;
12 }
13 }
14 for(int k = 0; k < mainRoad.size(); k++){
15 if((mainRoad.get(k).getName().equals("IN")||mainRoad.get(k).getName().equals("OUT"))&&road.equals(mainRoad.get(k).slaveLine)){
16 mainRoad.remove(k);//去掉存在于主路里的已经无效的IN和OUT
17 }
18 }
19 }
20 }
21 }
❉电阻计算的递归调用
电阻计算因为有分支一两次的路,所以必须分开计算,而且因为不同级的计算方法有嵌套,所以递归法计算属于一种很契合的解决方法
public static double setToR_add(LinkedList<LinkedList> alllist,String SSID) {//计算总线路的电阻
LinkedList<Double> allRoadR=new LinkedList<>();//存放同名从属路名的电阻
for(int i=0;i<alllist.size();i++){
LinkedList<circuit> list=alllist.get(i);
if(list.get(0).slaveLine.equals(SSID)){//仅对同名生效
double caclculateTotalResistance=0.0;
for(int j=0;j<list.size();j++){
if(list.get(j).getName().equals("IN")&&j!=0){//如果这条路还有下一级电路的接入口,则进行递归调用
if(list.get(j).deviceActivation){
caclculateTotalResistance += setToR_add(alllist, list.get(j).slaveLine);//递归调用
}
}else{
caclculateTotalResistance+=list.get(j).getResistance();
}
}
allRoadR.add(caclculateTotalResistance);
}
}
double totalResistance=0.0;
for(int i=0;i<allRoadR.size();i++){
if(allRoadR.get(i)!=0){
totalResistance+=1/allRoadR.get(i);//计算并联电路的电阻的倒数
}
}
if(totalResistance!=0){
return 1/totalResistance;//如果两条并联电路一断一开,那么相当于串联在了上一级路里,这样计算即使遇见这样的情况也可以正常返回
}else{
return 0;//全部断开
}
}
三、代码质量分析
---------------------------------------------------------------------------第七次大作业 SourceMonitor数据分析图

其实可以看出来的是,代码的单一职责做得还好
因为我把大部分的功能项都拆分成了单一职责的方法,那么这么做对于每次迭代的服用修改都方便
但是对于Max Complexity方法Tool.processingMainCircuit(),这里面包含了新类创建、计入链表、引脚正反接、互斥开关判断等等一堆的杂糅
所以说缝缝补补,代码的质量就会下降
---------------------------------------------------------------------------第八次大作业 SourceMonitor数据分析图

而最后一次迭代的代码质量相较上一次改变不大,但是方法的时间复杂度都有显著加深,因为递归本身的时间效率不是很高
而且代码主要处理逻辑都需要递归,并且我的代码里并不会记录保存重复的值,也就是说每用到一次就要重新计算一次
所以代码解决问题的能力存在,但是效率不高
更何况这次的代码还没成熟
四、两次迭代的总结与收获
总的来说,每次迭代,对于自身处理复杂逻辑能力都有加强
这次也是意识到了算法的重要性
做代码不仅要有严谨的逻辑,也要有更符合,更有效率的代码实现,也要有意识的去学习优秀的算法
这两次的大作业虽然建立在已有的代码结构上
但是对于封闭原则并没有多少实现
还是在原有的基础上修改添加,一步步地增加复杂度
所以接下来的学习重点就是在算法和逻辑上
本身Java就是一个内容庞大的知识体
那么肯定学的太浅层是做不了多少东西的

(正经事说完了,再说点不正经的)(bushi)
说真的,最后一次的迭代真的难炸天
因为本身题目的复杂程度就很大了,而且题目的测试样例也只有四个
其实说起来真的工作后问题是层出不穷的,但是既然是作业我觉得信息多一点也不是坏处
这次迭代有很大一部分问题就是我对并联套并联的结构不够特别清楚
多种组合和多种位置情况,二极管的通断,互斥开关的输出等
都是问题
都没解决
而且最近期末,复习的科目也多,Java催的也紧
所以最后一次迭代没写多少分
也是一个遗憾吧
浙公网安备 33010602011771号