一、前言
本次blog是针对发布题目集7和8的总结。
1、题目集7,只有一题,电路模拟程序3。
输入串联总电路、并联电路、并联电路分支电路信息后,程序要从连接信息中,提取设备信息如设备类型、输入引脚、输出引脚,最后输出控制设备的状态、受控设备的亮度、转速以及窗帘打开比例。与电路模拟程序2相比,
1)新增互斥开关。互斥开关有1、2、3三个引脚,一个汇总引脚,一个分支引脚。互斥开关只有两种状态:开关接往上面的2号引脚、接往下面的3号引脚。开关每次只能接通其中一个分支引脚,而另一个分支引脚处于断开状态。互斥开关的默认状态为1、2引脚接通,1、3引脚断开。
2)新增电器设备的电阻。互斥开关12引脚之间默认电阻为5,13引脚之间默认电阻为10,白炽灯的电阻为 10,日光灯的电阻为 5,吊扇的电阻为 20,落地扇的电阻为 20,窗帘电阻为15,其余设备电阻均为0。
3)新增受控设备。受控窗帘,受到光照强度、电压影响。
4)新增电路情况。线路中包含多个串联起来的并联电路,使得电路情况更加复杂。
2、题目集8,只有一题,电路模拟程序4。
输入依然是串联总电路、并联电路、并联电路分支电路信息,程序要从连接信息中,提取设备信息如设备类型、输入引脚、输出引脚,最后输出控制设备的状态、受控设备的亮度、转速以及窗帘打开比例。与电路模拟程序3相比,
1)新增管脚电压的显示。在输出每个电器的状态信息后,再依次输出该电器每个管脚的电压。
2)新增电流限制。各类电器的最大限定电流如下:开关20、分档调速器18、连续调速器18、白炽灯9、日光灯5、吊扇12、落地扇14、互斥开关20、受控窗帘12、二极管8,当实时电流超过最大电流时,在该电器输出信息的最后加入提示“exceeding current limit error”。
3)新增短路检测。如果电路出现无穷大的电流造成短路,所有元器件信息不输出,仅输出提示“short circuit error”。
4)新加控制设备二极管。其电路特性为:正向导通,反向截止,当电流从左至右流过时,二极管导通”conduction”,电阻为0;电流从右至左流动时,二极管截止”cutoff”,电阻无穷大,相当于开关打开。
5)新增电路情况。并联电路中包含并联,使电路解析更为复杂。
二、设计分析
设计合理简洁的结构图可以使代码编写变得简便,因此这部分对两次电路程序的结构、变量、方法进行分析,分别给出他们的SourceMontor的生成报表内容、类图、主函数和工作流程。
1、电路模拟程序7
1)SourceMontor的生成报表内容
从报表内容可以看到以下关键指标:文件代码总行1100,语句共586,百分比分支语句为30.2,方法调用语句为237,带注释行百分比为10.6,类和接口共14个,每个类的平均方法为1.93,每个类的平均语句数为7.78,最复杂方法共370行。
这些指标表明:代码结构相对简单,平均复杂度较低,方法普遍简短精炼注释充足,有利于代码维护类的划分合理,符合单一职责原则。
2)类图
以下是类图中每个类中方法的大致作用。
1))ElectricityEquipment(基类)
public void setInputVoltage(double inputVoltage):设置设备输入电压
public double getOutputVoltage():获取设备输出电压
public void printResult():打印设备状态信息
public double getResistance():获取设备电阻值
public void setParentCircuit(SeriesCircuit circuit):设置设备所属的父级串联电路
public SeriesCircuit getParentCircuit():获取设备所属的父级串联电路
2))ControlEquipment(控制设备抽象类)
public abstract double getOutputVoltage():抽象方法,由子类实现获取输出电压的具体逻辑
3))Switch(开关类)
public boolean getstatus():获取开关状态
public boolean changeSwitch():切换开关状态(开/关),返回切换后的状态
public double getOutputVoltage():根据开关状态返回输出电压,闭合时返回输入电压,打开时返回0
public void printResult():打印开关状态,closed/turned on
4))GradeGovernor(分档调速器)
public int upGearPosition():提高档位(最高3档),返回调整后的档位
public int downGearPosition():降低档位(最低0档),返回调整后的档位
public double getOutputVoltage():根据档位计算输出电压,0档:0V,1档:30%输入电压,2档:60%输入电压,3档:90%输入电压
public void printResult():打印当前档位
5))ContinuousGovernor(连续调速器)
public void setGearPosition(double GearPosition):设置档位(0.00-1.00之间的连续值)
public double getOutputVoltage():根据档位比例计算输出电压,输出电压 = 输入电压 × 档位比例
public void printResult():打印当前档位(保留两位小数)
6))ExclusiveSwitch(互斥开关)
public void setReversed(boolean reversed):设置开关安装方向
public boolean isReversed():获取开关安装方向
public void toggleSwitch():切换开关状态
public boolean isUpperConnected():获取开关连接状态(考虑安装方向)
public double getResistance():获取电阻值
public double getOutputVoltage():根据开关状态和安装方向计算输出电压
public void printResult():打印开关状态
7))ControlledEquipment(受控设备抽象类)
protected void calculateVoltageDifference():计算设备两端的电压差,考虑整个电路链的电压分配
private SeriesCircuit getTopLevelCircuit(SeriesCircuit circuit):获取最顶层的电路,用于电压计算
public double getOutputVoltage():计算并返回输出电压
8))Lamp(灯抽象类)
abstract double getBrightness():抽象方法,计算灯的亮度
public void printResult():打印灯的亮度(整数形式)
9))IncandescentLamp(白炽灯)
public double getBrightness():根据电压差计算亮度,0V时亮度为0,220V时亮度为200,其他电压:(150/210)(电压差-10)+50
10))SunshineLamp(日光灯)
public double getBrightness():计算亮度,有电压时亮度为180,无电压时亮度为0
11))Fan(风扇抽象类)
abstract double getRotateSpeed():抽象方法,计算风扇转速
public void printResult():打印风扇转速(整数形式)
12))CeilingFan(吊扇)
public double getRotateSpeed():计算转速,电压>150V:360转/分,80V≤电压≤150V:4(电压-80)+80转/分,电压<80V:0转/分
13))StandFan(落地扇)
public double getRotateSpeed():计算转速,电压≥140V:360转/分,120V≤电压<140V:260转/分,100V≤电压<120V:160转/分,80V≤电压<100V:80转/分,电压<80V:0转/分
14))ControlledCurtain(受控窗帘)
public void updateOpenRatio(double totalBrightness):根据总亮度更新窗帘开启比例
public void printResult():打印窗帘开启比例(百分比形式)
15))SeriesCircuit(串联电路)
public List
public double getResistance():计算串联电路总电阻
public void parseInput():解析输入命令并构建电路
public void handleSeriesCircuit(String line):处理串联电路配置命令
public void handleParallelConnection(String line):处理并联连接配置命令
public void handleSwitchCommand(String command):处理开关操作命令
public void handleGradeGovernorCommand(String command):处理分档调速器操作命令
public void handleContinuousGovernorCommand(String command):处理连续调速器操作命令
public void handleExclusiveSwitchCommand(String command):处理互斥开关操作命令
public void updateCurtains():更新所有窗帘的状态
public void propagateVoltage():传递和计算电路中的电压分配
public void updateEquipmentState(ElectricityEquipment equipment):更新设备状态
public void printResult():按顺序打印所有设备状态
private int getDeviceTypeOrder(String flag):获取设备类型的排序优先级
public ParallelCircuit getOrCreateParallelCircuit(String id):获取或创建并联电路对象
public ElectricityEquipment getOrCreateEquipment(String id):获取或创建设备对象
public ElectricityEquipment createEquipmentById(String id):根据ID创建相应的设备实例
16))ParallelCircuit(并联电路)
public List
public List
public void addSeriesCircuit(SeriesCircuit circuit):添加串联支路
public void setInputVoltage(double voltage):设置并联电路输入电压
public double getResistance():获取并联电路电阻
public double getEquivalentResistance():计算并联电路等效电阻
public double getOutputVoltage():计算并联电路输出电压
public void printResult():打印所有并联支路的状态
3)主函数
public class Main {
public static void main(String[] args) {
double initialVoltage = 220.0; // 设置初始电压
SeriesCircuit circuit = new SeriesCircuit(initialVoltage);
circuit.parseInput(); // 开始解析输入
}
}
4)工作流程
程序启动后首先创建主干路电路对象并设置220V的初始电压,然后进入命令处理循环:读取输入命令并根据命令前缀如电路配置或设备操作将其分发到相应的处理方法。对于电路配置命令,程序会处理串并联电路的配置并创建连接相应的设备;对于设备操作命令,则处理各类开关和调速器的状态变更。每次操作后,程序都会重新计算整个电路的电阻值,并按照串并联关系传递和分配电压,更新所有设备的工作状态包括根据总亮度特别计算窗帘的开启比例。最后,程序会按照预定的设备类型顺序,格式化输出所有设备的当前状态信息。这个循环会一直持续到程序结束。
2、电路模拟程序8
1)SourceMontor的生成报表内容
从报表内容可以看到以下关键指标:文件代码总行1644,语句共856,百分比分支语句为28.9,方法调用语句为406,带注释行百分比为10.8,类和接口共15个,每个类的平均方法为5.93,每个类的平均语句数为8.87,最复杂方法共481行。
这些指标表明:代码结构相对简单,平均复杂度较低,方法普遍简短精炼注释充足,有利于代码维护类的划分合理,符合单一职责原则。
2)类图
以下是类图中每个类中方法的大致作用。相比电路程序7,新增的方法:
1))ElectricityEquipment (基类)
isExceedingCurrentLimit(): 检查设备电流是否超过限制
setelectricity(): 设置设备电流值
setParentCircuit(): 设置设备所属的父级串联电路
2))SeriesCircuit (串联电路)
calculateCurrents(): 计算电路中各设备的电流
isShortCircuit(): 检查是否存在短路
isAllSwitchesClosed(): 检查所有开关是否都闭合
3))ParallelCircuit (并联电路)
calculateBranchCurrents(): 计算各并联支路的电流
propagateVoltageToAllBranches(): 向所有支路传递电压,考虑短路情况
4))Diode (二极管类):新增
updateState(): 更新二极管的导通状态
getResistance(): 根据导通状态返回电阻值
getOutputVoltage(): 根据导通状态计算输出电压
3)主函数
与电路程序7相同。
4)工作流程
与电路程序7类似。
三、踩坑心得
这部分对大作业中遇到的错误进行总结,在第一部分总结的迭代新增功能中,有很多编程时容易遇到很多错误。
1、电路模拟程序7
1))互斥开关的电阻处理
问题:最初没有考虑到互斥开关在不同连接状态下的电阻变化和一条连通则另一条断开。
解决:根据开关状态动态计算电阻,1-2接通时为5Ω则1-3断开,1-3接通时为10Ω则1-2断开,在并联计算总电阻要特殊处理互斥开关。
点击查看代码
for (SeriesCircuit circuit : parallelCircuits) {
boolean isPathConducting = true;
double pathResistance = 0;
// 检查支路是否导通并计算支路总电阻
for (ElectricityEquipment equipment : circuit.getDevices()) {
if (equipment instanceof Switch && !((Switch) equipment).getstatus()) {
isPathConducting = false;
break;
}
// 特殊处理互斥开关
if (equipment instanceof ExclusiveSwitch) {
ExclusiveSwitch exclusiveSwitch = (ExclusiveSwitch) equipment;
if (circuit == upperBranchCircuit) {
if (!exclusiveSwitch.isUpperConnected()) {
isPathConducting = false;
break;
}
} else if (circuit == lowerBranchCircuit) {
if (exclusiveSwitch.isUpperConnected()) {
isPathConducting = false;
break;
}
}
pathResistance += exclusiveSwitch.getResistance();
} else {
pathResistance += equipment.getResistance();
}
}
// 如果支路导通,将其电导加入总和
if (isPathConducting && pathResistance > 0) {
reciprocalSum += 1.0 / pathResistance;
}
}
2))电压分配计算
问题:在复杂的串并联电路中,电压分配计算不准确
解决:实现了递归的电压传递算法,考虑整个电路链的阻值比例
点击查看代码
public void propagateVoltage() {
double totalResistance = getResistance();
boolean hasOpenSwitch = mainCircuitDevices.stream()
.filter(device -> device instanceof Switch)
.map(device -> (Switch) device)
.anyMatch(sw -> !sw.getstatus());
if (hasOpenSwitch) {
allDevices.forEach(device -> {
device.setInputVoltage(0);
updateEquipmentState(device);
});
updateCurtains(); // 更新窗帘状态
return;
}
if (totalResistance > 0) {
double currentVoltage = inputVoltage;
for (ElectricityEquipment device : mainCircuitDevices) {
device.setInputVoltage(currentVoltage);
if (device instanceof ParallelCircuit) {
((ParallelCircuit) device).getOutputVoltage();
} else {
double voltageDrop = (device.getResistance() / totalResistance) * inputVoltage;
currentVoltage -= voltageDrop;
}
updateEquipmentState(device);
}
updateCurtains(); // 更新窗帘状态
}
}
3))窗帘控制逻辑
问题:窗帘开启比例的计算未考虑所有光源的累积效果
解决:增加了totalBrightness的计算,综合考虑所有灯的亮度,并在电压、灯亮计算完后,再设置窗帘的状态。
4))串联包含串联
问题:线路中包含多个串联起来的并联电路,它以子电路形式串联在主干路,解析时没有考虑这种情况
解决方法:对这种电路要特殊处理,在串联电路中加入当前设备所属的串联电路,解析时还有存贮相关信息。
点击查看代码
public void calculateVoltageDifference() {
if (parentCircuit != null) {
// 获取当前设备所在的直接父电路的总电阻
double totalResistance = parentCircuit.getResistance();
// 如果父电路还有父电路,需要考虑整个电路链的电压分配
SeriesCircuit currentCircuit = parentCircuit;
double availableVoltage = inputVoltage;
while (currentCircuit.getParentCircuit() != null) {
// 获取上一级电路的总电阻
double upperTotalResistance = currentCircuit.getParentCircuit().getResistance();
// 计算当前电路在上一级电路中分得的电压
if (upperTotalResistance > 0) {
availableVoltage = (currentCircuit.getResistance() / upperTotalResistance) *
currentCircuit.getParentCircuit().inputVoltage;
}
currentCircuit = currentCircuit.getParentCircuit();
}
// 使用实际可用电压计算当前设备的电压差
if (totalResistance > 0) {
VoltageDifference = (resistance / totalResistance) * availableVoltage;
outputVoltage = inputVoltage - VoltageDifference;
} else {
VoltageDifference = 0;
outputVoltage = inputVoltage;
}
}
}
// 获取最顶层电路的辅助方法
public SeriesCircuit getTopLevelCircuit(SeriesCircuit circuit) {
SeriesCircuit current = circuit;
while (current.getParentCircuit() != null) {
current = current.getParentCircuit();
}
return current;
}
if (firstSection.startsWith("VCC")) {
// 主干路处理逻辑保持不变
mainCircuitDevices.clear();
for (String section : sections) {
section = section.replace("[", "").replace("]", "").trim();
String[] parts = section.split(" ");
if (parts[0].equals("VCC")) {
continue;
}
// 获取设备ID,考虑连接点
String deviceId;
if (parts[0].contains("-")) {
deviceId = parts[0].split("-")[0];
} else if (parts[1].contains("-")) {
deviceId = parts[1].split("-")[0];
} else {
continue;
}
// 处理T开头的子电路引用
if (deviceId.startsWith("T") && equipmentMap.containsKey(deviceId)) {
SeriesCircuit subCircuit = (SeriesCircuit) equipmentMap.get(deviceId);
subCircuit.setParentCircuit(this);
mainCircuitDevices.add(subCircuit);
} else {
// 普通设备的处理保持不变
ElectricityEquipment device = getOrCreateEquipment(deviceId);
device.setParentCircuit(this);
mainCircuitDevices.add(device);
if (!allDevices.contains(device)) {
allDevices.add(device);
}
}
}
2、电路模拟程序8
1)短路检测
问题:复杂电路的情况,电路有很多种情况短路。
解决:增加了isShortCircuit()方法,检查总电阻是否为0
点击查看代码
public boolean isShortCircuit() {
// 检查是否有开关断开电路
boolean hasOpenSwitch = mainCircuitDevices.stream()
.filter(device -> device instanceof Switch)
.map(device -> (Switch) device)
.anyMatch(sw -> !sw.getstatus());
// 如果有开关断开,电路是断开的而不是短路
if (hasOpenSwitch) {
return false;
}
// 计算总电阻
double totalResistance = getResistance();
return totalResistance == 0;
}
2))电流限制检测
问题:电流计算不准确导致错误的超限提示
解决:在每个设备中增加limitedelectricity属性,处理完电路分压后,计算出每个设备的电流,分析是否超出限制了。
点击查看代码
public boolean isExceedingCurrentLimit() {
return electricity > limitedelectricity;
}
3))二极管的状态处理
问题:二极管的导通/截止状态判断不准确
解决:根据输入电压和引脚方向动态更新二极管状态
点击查看代码
public Diode(String equipmentFlag, int equipmentId, int inputPin, int outputPin, double inputVoltage) {
super(equipmentFlag, equipmentId, inputPin, outputPin, inputVoltage);
this.resistance = 0.0; // 初始电阻为0
this.limitedelectricity = 8.0;
updateState();
}
public void updateState() {
// 检查输入电压和引脚方向
if (inputPin == 1) {
isForwardBiased = inputVoltage > 0;
} else {
isForwardBiased = inputVoltage < 0;
}
state = isForwardBiased ? "conduction" : "cutoff";
resistance = isForwardBiased ? 0.0 : Double.POSITIVE_INFINITY;
}
@Override
public double getOutputVoltage() {
updateState();
outputVoltage = isForwardBiased ? inputVoltage : 0;
return outputVoltage;
}
@Override
public double getResistance() {
updateState();
return resistance;
}
4))并联中的并联结构
问题:嵌套并联结构导致电压传递混乱
解决:在handleSeriesCircuit、handleParallelConnection函数解析串联电路、并联电路时特殊处理这种连接电路,确保正确处理多层并联。
四、改进建议
三次大作业的答题程序都离满分很大距离,因此程序还有很多问题,还存在很大的进步空间。
1、电路模拟程序3
1)单并联电路-含互斥开关测试样例中,有很多错误,所以互斥开关的检测逻辑还需改善;
2)多并联电路测试样例也有很多没对,即解析串联和并联电路还需要改善,要正确识别电路设备、输入引脚和输出引脚信息。
2、电路模拟程序3
1)电路连接情况复杂,很多造成短路的情况没有考虑如串联电路+并联电路时,并联电路一条断开一条闭合也可能造成短路,检查短路函数isShortCircuit()还需要改善。
2)并联包含并联等复杂电路测试样例错误,即解析电路函数还需要改善。
3)二极管电器特性特殊,在复杂电路中会极大影响电路,所以有关二极管处理还需要改善。
五、总结
通过电路模拟程序3和4的开发,我获得了以下重要收获:
1、技术能力提升
面向对象编程能力得到提升,深入理解了继承和多态的实际应用,如设备基类和各类具体设备的实现,掌握了抽象类的设计技巧,如ControlEquipment和ControlledEquipment,学会了接口设计原则,确保了代码的可扩展性,复杂算法的实现,学习了处理复杂的递归结构如多层嵌套的串并联电路,提高了对边界条件的处理能力,如短路检测、电流限制等,学会了如何设计清晰的类层次结构,了解了模块化编程的重要性,理解了代码复用的便利性,设计好程序的结构才能顺利开发出程序。
2、问题解决能力
掌握了一些调试技巧,学会了使用断点调试,提高了代码测试的系统性思维。学会了更全面的异常处理方式,提高了对边界情况的考虑,多写注释可以增强了代码的健壮性也便于理解。
3、项目经验
电路程序的需求不断在迭代,需要从之前的代码继续增加功能。增强了需求分析能力,学会了如何分析复杂的业务需求,提高了如何将业务需求转化为代码的能力,理解了渐进式开发的重要性,程序复用的重要性。
电路程序7、8都比较困难,编程遇到很多问题,电路连接情况、短路情况都需要考虑很多,在不断改正下,让我收获很多,得到很多锻炼。