目录
前言
PTA第七次作业
设计与分析
题目分析
本题在家居强电电路模拟程序-2基础上新增了多个并联电路串联在一起的情况。需要虑考虑条串联电路中包含其他串联电路的情况。同时还添加了互斥开关、受控窗帘。输入格式,输出信息也有改变。
输入格式:
1.输入设备信息
分别用设备标识符K、F、L、B、R、D、A、H、S分别表示开关、分档调速器、连续调速器、白炽灯、日光灯、吊扇、落地扇、互斥开关、受控窗帘。设备标识用标识符+编号表示,引脚使用设备标识-引脚编号。
2.输入连接信息
格式:"["+引脚号+" "+引脚号+"]"
3.输入控制设备调节信息
开关、互斥开关调节信息格式:
#+设备标识K+设备编号
#+设备标识H+设备编号
分档调速器的调节信息格式:
#+设备标识F+设备编号+"+"
#+设备标识F+设备编号+"-"
连续调速器的调节信息格式:
#+设备标识L+设备编号+":" +数值
4.标识:VCC,电压220V。GND,电压0V。输入信息以end为结束标志。
5.输入串联电路信息
串联电路信息格式:"#T"+电路编号+":"+连接信息+" "+连接信息+...+" "+连接信息
6.输入并联电路信息
"#M"+电路编号+":"+”[”+串联电路信息+" "+....+" "+串联电路信息+”]”
输出格式:
按开关、分档调速器、连续调速器、白炽灯、日光灯、吊扇、互斥开关、受控窗帘的顺序依次输出所有设备的状态或参数。每个设备一行。同类设备按编号顺序从小到大输出。
@设备标识+设备编号+":" +设备参数值(控制开关的档位或状态、灯的亮度、风扇的转速,只输出值,不输出单位)
连续调速器的档位信息保留两位小数,即使小数为0,依然显示两位小数.00。
开关状态为0(打开)时显示turned on,状态为1(合上)时显示closed
分析:
输入相关限制
电路结构方面:
本次迭代不考虑并联电路中包含并联电路这种复杂嵌套情况,但允许多个并联电路串联在一起,同时也考虑一条串联电路中包含其他串联电路的情况,例如所举示例那样的结构是在考虑范围内的。
输入的串联电路信息要在并联信息之前,且不考虑乱序输入,有着明确的输入顺序要求。
电压范围限制:不考虑输入电压或电压差超过 220V 的情况,明确了电压的合理范围边界。
结束标志规定:输入信息以 “end” 为结束标志,在此之后的输入信息会被忽略,便于界定有效输入内容。
短路情况限定:只要不因短路而造成无穷大的电流烧坏电路(像部分短接等情况)都是合理的、在测试点考虑范围内,而会造成无穷大电流的短路情况在本次迭代中不考虑,对电路故障相关情形做了范围划分。
输出相关要求
输出设备顺序及格式:
需要按开关、分档调速器、连续调速器、白炽灯、日光灯、吊扇、互斥开关、受控窗帘这样固定的顺序依次输出所有设备的状态或参数,每个设备单独占一行,且同类设备按编号顺序从小到大输出。
输出格式有着严格规范,不同类型设备按相应规则输出参数值,例如开关显示对应状态的文字表述、连续调速器的档位信息保留两位小数、互斥开关显示引脚接通状态、受控窗帘显示打开百分比等。
通用默认规则
数值计算及输出处理:在计算电压值等数值过程中,最终结果若出现小数,采用截尾规则去掉小数部分只保留整数部分,不过在计算过程中要用 double 类型保存并计算相关可能出现小数的数值(如电压、转速、亮度等),仅在最后输出时按规则处理。
电路连接信息顺序:所有连接信息按电路从电源到接地的顺序依次输入,电源 “VCC” 一定是第一个连接的第一项,接地 “GND” 一定是最后一个连接的后一项,并且如果连接信息只包含两个引脚,也是靠电源端的引脚在前,靠接地端的在后,确保了电路连接信息输入的规范性和逻辑性。
调速器连接限制:调速器的输入端只会直连 “VCC”,整个电路最多只有连接在电源上的一个调速器,且不包含在并联单路中,对调速器在电路中的连接情况进行了明确约束。
知识点解析
1.继承关系:
Series、Parallel、SwitchDevice 等类都继承自 Equipment 类,继承使得子类可以复用父类的属性(如 equipmentType、equipId 等)和方法(如 showmethod 等),同时又能根据自身特点扩展新的属性和方法。例如 SwitchDevice 类在继承 Equipment 基础上新增了 switchState 属性和 changestate 等特有的方法,用于表示和操作开关设备的开合状态。
2.多态性体现:
在 updateInvBasedOnType 方法中,通过判断设备类型(equipment.equipmentType 的值)来调用不同子类重写的 changeOutputVoltage 方法(如 SwitchDevice、StepSpeedRegulator、ConAdjustDevice 等子类各自的实现),根据对象的实际类型来执行相应的行为,提高了代码的灵活性和可扩展性。
3.集合相关接口和类( Set、ArrayList):
Set 接口的实现(通过 Set.of 创建的集合)用于判断输入信息是否属于特定控制前缀集合,在 isControlInput 方法中,利用 Set 的特性方便地进行元素是否存在的判断,避免了使用复杂的条件判断逻辑来对比多个控制前缀字符串。
ArrayList 在 displayEquipmentInfo 方法中用于辅助处理互斥开关(H 类型设备)的输出逻辑,通过记录已输出的互斥开关的唯一标识来避免重复输出,发挥了其动态数组便于元素添加和查询的特点。
4.LinkedList 和 LinkedHashMap 的使用:
LinkedList 于存储各种设备相关的标识列表,例如 EquipmentIDList、ConnectionList 等,其优势在于方便元素的插入和删除操作,适合按顺序处理输入信息以及动态管理设备相关的标识集合。
LinkedHashMap 用于存储设备对象与对应的标识映射关系,像 EquipmentMap,它既能保证元素按照插入顺序进行存储,又方便通过键(设备标识)快速获取对应的设备对象,在需要对设备进行查找、更新等操作时非常实用,例如 EquipmentMap.put(serial_name, tandem); 实现了将串联设备对象添加到映射中以便后续根据标识来访问该对象。
5.电路状态更新逻辑:
在多个方法中实现了对电路状态的分析和更新,比如 Series 类中的 updateSeriesState 方法,通过遍历串联电路中的各个设备(开关设备、互斥开关、子串联电路等情况)来综合判断该串联电路整体的导通状态(通过 seriesState 属性表示),涉及到复杂的条件判断和状态流转逻辑,以模拟真实电路中串联部分的通断情况。
类似地,Parallel 类的 updateResistanceValue 方法根据并联电路中各串联支路(通过判断设备是否为 Series 且其状态等情况)来计算并联电路的总电阻,这些都是基于电路基本原理在代码中的逻辑实现,用于后续对电路中各设备电压、参数等的进一步计算。
6.工厂模式的初步应用:
EquipmentFactory 类可以看作是简单工厂模式的一种体现,它通过一个静态的 Map 存储了不同设备标识对应的设备类信息,然后根据输入的设备标识利用反射机制(通过 newInstance 方法创建对象实例)来创建相应的设备对象,将对象创建逻辑集中管理,方便代码的扩展和维护,例如后续添加新的设备类型,只需在 equipmentClasses 映射中添加对应的标识和类关系即可,而不需要在多处创建对象的地方进行修改。
7.比较器接口实现排序逻辑:
comparator 类实现了 Comparator 接口,通过自定义比较规则来对设备列表进行排序,按照题目要求规定的设备类型顺序(先开关、再调速器等)以及同类型设备编号从小到大的顺序来排列设备,使得最终输出设备信息时能符合特定的顺序要求,这是运用比较器接口来实现自定义排序逻辑的常见做法,有助于保证输出的规范性和可读性。
调试过程
1.类的定义分析
Equipment
类
- 概述:所有设备类的基类,定义了一些通用的属性和方法,用于描述家居电路中设备的基本特征与行为。
- 属性:
double resistanceValue
:用于表示设备的电阻值,初始化为 0,不同的具体设备子类有不同的电阻值设定。double inVoltage
:记录设备的输入电压,在电路中随着电流流经不同设备以及开关等状态变化。double outVoltage
:设备的输出电压,会根据设备的具体功能和当前输入电压以及自身状态来确定其具体数值。String equipmentType
:标识设备的类型。String equipId
:设备的唯一标识符。
- 方法:
public void showmethod(){}
:空方法,定义一个统一的展示设备信息的接口,具体逻辑由各个子类去重写实现。public Equipment(String equipmentType, String equipId)
:构造函数,用于创建Equipment
类的对象实例时初始化equipmentType
和equipId
这两个重要属性,确保每个设备对象都有明确的类型和标识。
Series
类
- 概述:继承自
Equipment
类,用于表示串联电路部分。 - 属性:
LinkedHashMap <String,Equipment> seriesEquipMap
:以设备标识为键,对应的Equipment
类型设备对象为值的映射,用于存储串联电路中包含的各个设备。int seriesState
:表示串联电路的整体状态,初始化为 0,通过updateSeriesState
方法根据串联电路中各个设备的状态来更新这个值,若为 1 则表示串联电路处于导通状态。LinkedList <String> seriesEquipIdList
:存储串联电路中包含的各个设备的标识。String regex1
和String regex2
:定义了两个正则表达式字符串,用于匹配互斥开关(mutex_switch
)中特定引脚相关的设备标识情况,辅助判断互斥开关对串联电路状态的影响。
- 方法:
public Series(String Series_id)
:构造函数,调用父类(Equipment
)的构造函数传入"S"
(表示串联类型)和给定的串联电路标识Series_id
来初始化对象,同时将seriesState
初始化为 0。public void updateSeriesState()
:核心方法,用于更新串联电路的状态。
Parallel
类
- 概述:继承自
Equipment
类,用于表示并联电路部分。 - 属性:
LinkedList<String> paraEquipIdList
:存储并联电路中所包含的各个设备的标识。LinkedHashMap<String,Equipment> paraEquipMap
:以设备标识为键,对应的Equipment
类型设备对象为值的映射,和串联电路中的类似映射作用相似。
- 方法:
public Parallel(String Parallel_id)
:构造函数,调用父类构造函数传入"P"
(表示并联类型)和给定的并联电路标识Parallel_id
来初始化对象,同时将resistanceValue
(电阻值)初始化为 0,后续会通过updateResistanceValue
方法根据并联电路中各支路(通常是串联电路)的情况来计算并更新这个电阻值。public void updateResistanceValue()
:该方法用于计算并联电路的总电阻值。。
SwitchDevice
类
- 概述:继承自
Equipment
类,表示电路中的开关设备。 - 属性:
int switchState
:用于记录开关设备的当前状态,0 表示打开(断路),1 表示合上(导通),初始化为 0,通过changestate
方法可以改变其状态。
- 方法:
public SwitchDevice(String equipmentid)
:构造函数,调用父类构造函数传入"K"
(表示开关类型)和给定的设备标识equipmentid
来初始化对象,同时初始化switchState
和resistanceValue
。public void changestate()
:用于改变开关的状态,通过简单的逻辑将当前状态取反(switchState = (oldState == 0)? 1 : 0;
),实现开关的开合切换操作。public double changeOutputVoltage(double inputVoltage)
:根据开关当前的状态来确定输出电压。@Override public void showmethod()
:重写了父类的showmethod
方法,按照规定格式输出开关设备的状态信息。
StepSpeedRegulator
类
- 概述:继承自
Equipment
类,代表分档调速器设备。 - 属性:
int speedLevel
:记录调速器的当前档位,初始化为 0,通过handleControl
方法可以根据接收到的控制命令来调整档位值。double outputVoltage
:调速器的输出电压,会根据当前档位和输入电压按照特定的比例关系进行计算更新,用于后续影响与之相连的设备的工作状态。double resistanceValue
:调速器的电阻值,初始化为 0。
- 方法:
public StepSpeedRegulator(String equipmentid)
:构造函数,调用父类构造函数传入"F"
(表示分档调速器类型)和给定的设备标识equipmentid
来初始化对象,同时初始化speedLevel
和resistanceValue
这两个属性。public double changeOutputVoltage(double inputVoltage)
:根据当前的调速档位speedLevel
来计算并返回输出电压。不同的档位对应不同的与输入电压的比例关系。public void handleControl(String cin, Map<String, Equipment> EquipmentMap)
:用于处理接收到的控制命令字符串,通过解析字符串中的关键信息,在设备映射EquipmentMap
中查找对应的调速器对象,实现对调速器的外部控制逻辑。@Override public void showmethod()
:重写父类的showmethod
方法,按照规定格式输出调速器的当前档位信息。
ConAdjustDevice
类
- 概述:继承自
Equipment
类,用于表示连续调速器设备。 - 属性:
double adjustParameter
:表示连续调速器的调节参数,范围在 0 到 1 之间,初始化为 0,通过setparameter
方法可以设置该参数值,进而影响输出电压。
- 方法:
public ConAdjustDevice(String id)
:构造函数,调用父类构造函数传入"L"
(表示连续调速器类型)和给定的设备标识id
来初始化对象,同时初始化adjustParameter
和resistanceValue
(电阻值设为 0)属性。public void setparameter(double p)
:用于设置连续调速器的调节参数值,直接将传入的参数值赋给adjustParameter
属性,以便后续根据该参数计算输出电压等操作。public double changeOutputVoltage(double inputVoltage)
:根据当前的调节参数adjustParameter
来计算并返回输出电压。@Override public void showmethod()
:重写父类的showmethod
方法,按照规定格式输出连续调速器的调节参数信息。
WhiteLightDevice
类
- 概述:继承自
Equipment
类,代表白炽灯设备,有表示灯光亮度的属性以及根据输入电压设置灯光亮度、输出亮度信息的方法,用于模拟白炽灯在电路中的工作状态变化。 - 属性:
double Whitelight
:用于记录白炽灯的亮度值,初始化为 0,通过setWhitelight
方法根据输入电压来确定并更新亮度值,最终按照规则输出展示给用户。
- 方法:
public WhiteLightDevice(String id)
:构造函数,调用父类构造函数传入"B"
(表示白炽灯类型)和给定的设备标识id
来初始化对象,同时初始化Whitelight
属性并设置电阻值为 10(代表白炽灯的一个固有电阻特性设定)。public void setWhitelight(double inputVoltage)
:按照不同的输入电压区间来计算并设置白炽灯的亮度值。@Override public void showmethod()
:重写父类的showmethod
方法,按照规定格式输出白炽灯的亮度值。
SunLightDevice
类
- 概述:继承自
Equipment
类,用于表示日光灯设备,有表示光照强度的属性以及根据输入电压设置光照强度、输出强度信息的方法,模拟日光灯在电路中的工作表现。 - 属性:
double Sunlight
:记录日光灯的光照强度值,初始化为 0,通过setSunlight
方法根据输入电压来确定并更新该值,以便后续输出展示。
- 方法:
public SunLightDevice(String id)
:构造函数,调用父类构造函数传入"R"
(表示日光灯类型)和给定的设备标识id
来初始化对象,同时初始化Sunlight
属性并设置电阻值为 5(体现日光灯的固有电阻特性设定)。public void setSunlight(double inputVoltage)
:根据输入电压来设置日光灯的光照强度。@Override public void showmethod()
:重写父类的showmethod
方法,按照规定格式输出日光灯的光照强度值。
FanDevice
类
- 概述:继承自
Equipment
类,代表吊扇设备,有记录风扇转速的属性以及根据输入电压设置转速、输出转速信息的方法,用于模拟吊扇在电路中的转速变化情况。 - 属性:
double rotationSpeed
:用于记录吊扇的转速值,初始化为 0。
- 方法:
public FanDevice(String id)
:构造函数,调用父类构造函数传入"D"
(表示吊扇类型)和给定的设备标识id
来初始化对象,同时初始化rotationSpeed
属性并设置电阻值为 20(代表吊扇的固有电阻特性设定)。public void setRotationSpeed(double inputVoltage)
:按照不同的输入电压区间来计算吊扇的转速。。@Override public void showmethod()
:重写父类的showmethod
方法,按照规定格式输出吊扇的转速值。
GroundFanDevice
类
- 概述:继承自
Equipment
类,落地扇。 - 属性:
double rotationSpeed
:记录该类型风扇(落地扇等)的转速值,初始化为 0,通过setRotationSpeed
方法根据输入电压按照其特定的规则来计算并更新该值,用于后续输出展示。 - 方法:
public GroundFanDevice(String id)
:构造函数,调用父类构造函数传入 "A"(表示这类风扇类型)和给定的设备标识 id 来初始化对象,同时初始化rotationSpeed
属性并设置电阻值为 20(代表该风扇的固有电阻特性设定)。
public void setRotationSpeed(double inputVoltage)
:核心功能是根据输入电压的不同取值范围来设置风扇的转速。采用了嵌套的三元表达式来构建逻辑判断。
@Override public void showmethod()
方法:重写了父类Equipment
中的showmethod
方法,目的是按照规定的格式输出风扇设备的相关信息。。
mutex_switch
类
- 概述:继承自
Equipment
类,用于表示互斥开关设备,。 - 属性:
String switchState
:用于表示互斥开关当前的状态,会根据引脚接通情况更新为"turned on"
(断开)或"closed"
(接通),以此体现其在电路中的通断情况。boolean isOnSecondary
:表示互斥开关的二级引脚是否接通。boolean isOnTertiary
:表示互斥开关的三级引脚是否接通。
- 方法:
public mutex_switch(String id)
:构造函数,调用父类构造函数传入"H"
(表示互斥开关类型)和给定的设备标识id
来初始化对象,同时初始化isOnSecondary
和isOnTertiary
属性,确定初始的引脚接通状态。public void toggleSwitchState()
:用于切换互斥开关的状态,通过异或操作(^=
)来对isOnSecondary
和isOnTertiary
进行取反,实现引脚接通情况的切换,进而改变整个互斥开关的通断逻辑,模拟实际互斥开关的操作效果。public void updateSwitchStatus()
:根据当前isOnSecondary
的状态来更新互斥开关的相关属性。@Override public void showmethod()
:重写父类的showmethod
方法,按照规定格式输出互斥开关的状态信息,,展示互斥开关在电路中的当前状态。
Shutter
类
- 概述:继承自
Equipment
类,代表受控窗帘设备。 - 属性:
String shutterOpenness
:用于表示受控窗帘打开的百分比情况,初始为空,通过adjustShutterOpenness
方法根据光照强度等条件来确定并更新其值,最终按照格式输出展示。
- 方法:
public Shutter(String id)
:构造函数,调用父类构造函数传入"S1"
(表示受控窗帘类型)和给定的设备标识id
来初始化对象,同时设置电阻值为15
,作为受控窗帘的一个固有属性设定(可能与电路控制的一些特性相关)。public void adjustShutterOpenness(double lightIntensity)
:按照不同的光照强度区间来设置受控窗帘的打开百分比。@Override public void showmethod()
:重写父类的showmethod
方法,按照规定格式输出受控窗帘的打开百分比信息。
comparator
类
- 概述:实现了
Comparator
接口,用于定义设备之间的比较规则。 - 属性:
private static final List<Character> orderChars
:定义了一个不可变的字符列表,包含了表示设备类型的字符,按照规定的顺序排列,即'K'
(开关)、'F'
(分档调速器)、'L'
(连续调速器)、'B'
(白炽灯)、'R'
(日光灯)、'D'
(吊扇)、'A'
(另一种风扇,如落地扇)、'H'
(互斥开关)、'S'
(受控窗帘),这个顺序规定了不同类型设备在排序时的先后顺序。private static final java.util.Map<Character, Integer> orderMap
:通过静态代码块初始化的一个映射,将orderChars
中的每个设备类型字符与一个对应的整数序号关联起来,方便后续在比较方法中根据这个序号来快速判断不同设备类型的顺序关系,实现排序逻辑。
- 方法:
@Override public int compare(Equipment firstEquipment, Equipment secondEquipment)
:实现了Comparator
接口的compare
方法,用于比较两个Equipment
类型的对象(也就是不同的设备)。首先获取两个设备标识的首字符(对应设备类型字符),然后通过orderMap
查找对应的序号,如果序号不同则根据序号差值返回比较结果,确定两个设备的先后顺序;如果序号相同(即同类型设备),则进一步通过设备标识的字符串比较(EquipmentId1.compareTo(EquipmentId2)
)来确定顺序,最终使得设备列表能够按照规定的先类型后编号的顺序进行排序,便于符合要求的输出展示。
EquipmentFactory
类
- 概述:简单工厂类,运用了工厂模式的思想,负责根据给定的设备标识创建相应类型的具体设备对象,将对象创建的逻辑集中管理。
- 属性:
private static final Map<String, Class<? extends Equipment>> equipmentClasses
:定义了一个静态的映射,键是表示不同设备类型的正则表达式字符串(用于匹配相应的设备标识),值是对应的具体设备类(继承自Equipment
类的各类具体设备子类),通过这种映射关系,根据输入的设备标识来查找并创建对应的设备对象实例。
- 方法:
public static Equipment createEquipment(String eid)
:工厂类的核心方法,通过遍历equipmentClasses
映射中的每个键值对,使用正则表达式(Pattern.matches
)来判断输入的设备标识eid
是否匹配某个键(设备类型正则表达式)。
EquipmentUtils
类
- 概述:提供了一些工具方法,用于处理串联电路(
processTSeries
方法)和并联电路(processMParallel
方法)相关的设备映射关系构建,辅助完善整个家居电路模型中设备之间的关联逻辑。 - 方法:
public static void processTSeries(Map<String, Equipment> series_EquipmentMap, String device_id)
:如果传入的设备标识device_id
匹配串联电路的标识格式(T\\d+
),则从给定的串联电路设备映射series_EquipmentMap
中获取对应的串联电路对象(Series
类型),然后遍历该串联电路对象的设备标识列表(seriesEquipIdList
),将列表中的每个设备标识对应的设备对象添加到串联电路对象的seriesEquipMap
中。public static void processMParallel(Map<String, Equipment> parallel_EquipmentMap, String device_id)
:当设备标识device_id
匹配并联电路的标识格式(M\\d+
)时,从给定的并联电路设备映射parallel_EquipmentMap
中获取对应的并联电路对象(Parallel
类型),接着遍历该并联电路对象的设备标识列表(paraEquipIdList
),将列表中的设备标识对应的设备对象添加到并联电路对象的paraEquipMap
中。
EquipmentHandler
类
- 概述:了针对串联电路和并联电路相关处理的方法。
- 方法:
public static void processTSeries(Map<String, Equipment> EquipmentMap, String device_id)
:当设备标识device_id
符合串联电路标识格式(T\\d+
)时,从给定的设备映射EquipmentMap
中获取对应的串联电路对象(Series
类型),然后遍历该串联电路对象的设备标识列表(seriesEquipIdList
),针对每个设备标识判断其有效性(是否在EquipmentMap
中存在且设备类型不是H
(互斥开关))。public static void processMParallel(Map<String, Equipment> EquipmentMap, String device_id)
:对于符合并联电路标识格式(M\\d+
)的设备标识device_id
,从设备映射EquipmentMap
中获取对应的并联电路对象(Parallel
类型),然后调用该并联电路对象的updateResistanceValue
方法,根据并联电路中各设备(通常是串联支路)的状态等情况来更新并联电路的总电阻值。
2.重点代码代码具体分析
电路结构构建部分
初始化相关数据结构:
创建多个 LinkedList 和 LinkedHashMap,如 EquipmentIDList 用于存储设备标识符,ConnectionList 存储连接关系,ControlList 存放控制命令等,同时创建 Scanner 用于读取用户从控制台输入的内容,为后续构建电路模型做准备。
LinkedList<String> EquipmentIDList = new LinkedList<>();
LinkedList<String> ConnectionList = new LinkedList<>();
LinkedList<String> ControlList = new LinkedList<>();
LinkedList<Parallel> ParallelList = new LinkedList<>();
LinkedList<Series> SeriesList = new LinkedList<>();
LinkedHashMap<String,Equipment> EquipmentMap = new LinkedHashMap<>();
处理输入构建电路模型(基于 while 循环读取输入):
处理控制命令输入:
通过 isControlInput 方法判断输入是否是以特定控制前缀(#K、#L、#F、#H)开头的控制命令,如果是则添加到 ControlList 中,后续统一处理这些控制操作。
处理串联电路输入(以 #T 开头的输入):
使用正则表达式匹配解析输入内容,提取串联电路的标识以及包含的设备标识信息,创建 Series 类对象来表示串联电路,将设备标识添加到对应串联电路对象的相关列表(seriesEquipIdList)和映射(seriesEquipMap)中,同时把串联电路对象放入 SeriesList 以及 EquipmentMap 中,以此构建起串联电路的结构以及其与各设备之间的关联关系。
else if (entry.startsWith("#T")) {
if (matcher0.find()) {
String serial_name = matcher0.group(1);
EquipmentIDList.add(serial_name);
Series tandem = new Series(serial_name);
while (matcher1.find()) {
String connection = matcher1.group(0);
ConnectionList.add(connection);
String device_id = matcher1.group(1);
if (!device_id.isEmpty() && device_id.charAt(0) == 'H')
{tandem.seriesEquipIdList.add(connection);
EquipmentList.add(connection);
EquipmentIDList.add(connection);
} else {tandem.seriesEquipIdList.add(device_id);
EquipmentList.add(device_id);
EquipmentIDList.add(device_id);
}}SeriesList.add(tandem);EquipmentMap.put(serial_name, tandem);
}}
处理并联电路输入(以 #M 开头的输入):
同样利用正则表达式对输入进行处理,获取并联电路标识和其中包含的设备标识,创建 Parallel 类对象,整理设备标识信息添加到对应对象的 paraEquipIdList 等结构中,并放入 ParallelList 和 EquipmentMap,完成并联电路部分的初始化设置。
else if(entry.startsWith("#M")) {
String regex2 = "#(M\\d+):(.*)";String regex22 = "[\\[\\]]";String regex222 = "\\s+";String replacement = "";
Pattern pattern2 = Pattern.compile(regex2);Matcher matcher2 = pattern2.matcher(entry);
String strAfterColon = entry.substring(entry.indexOf(":") + 1, length - 1);
String processedStr = strAfterColon.replaceAll(regex22, replacement);
String[] splitArray = processedStr.split(regex222);
if (matcher2.find()){
String parallel_name = matcher2.group(1);
Parallel shunt = new Parallel(parallel_name);
EquipmentIDList.add(parallel_name);
ParallelList.add(shunt);
Collections.addAll(shunt.paraEquipIdList, splitArray);
EquipmentMap.put(parallel_name, shunt);
}}
设备控制命令处理部分
处理开关控制命令(以 #K 开头):
从 ControlList 中取出控制命令,若命令以 #K 开头,截取设备标识后在 EquipmentMap 中查找对应的 SwitchDevice 类型的开关设备,若找到则调用其 changestate 方法切换开关状态,实现对开关设备的控制操作。
处理互斥开关控制命令(以 #H 开头):
对于以 #H 开头的控制命令,提取关键部分用于匹配 EquipmentMap 中的互斥开关(mutex_switch)设备,通过正则表达式匹配找到对应互斥开关后,调用其 toggleSwitchState 方法来切换互斥开关的引脚接通等状态,模拟互斥开关在电路中的控制行为。
处理连续调速器控制命令(以 #L 开头):
命令以 #L 开头时,解析出设备标识和要设置的参数值,在 EquipmentMap 中查找对应的 ConAdjustDevice 连续调速器设备,找到后调用其 setparameter 方法设置调速参数,从而实现对连续调速器的控制,改变其输出电压等相关特性。
处理分档调速器控制命令(以 #F 开头):
当控制命令以 #F 开头,先判断在 EquipmentMap 中是否存在对应设备,若存在且类型是 StepSpeedRegulator 分档调速器,则调用其 handleControl 方法,根据命令中的具体指示(如升档、降档信息)来调整调速器的档位,实现对分档调速器的灵活控制。
电路状态更新部分
完善电路设备关联(调用 EquipmentUtils 相关方法):
通过循环遍历所有设备标识符(newEquipmentIDList),分别调用 EquipmentUtils 类中的 processTSeries 和 processMParallel 方法,这两个方法会将串联电路和并联电路中涉及的设备准确添加到对应电路对象的设备映射中,完善电路与设备之间的关联关系,确保后续基于完整准确的电路结构进行状态计算等操作。
更新串联电路状态(遍历 SeriesList 调用 updateSeriesState 方法):
遍历 SeriesList 中的每个串联电路对象,调用其 updateSeriesState 方法,就将串联电路状态设为导通,为后续基于电路通断状态进行设备参数计算奠定基础。
更新电路电阻等参数(调用 EquipmentHandler 相关方法):
再次循环遍历设备标识符,调用 EquipmentHandler 类中的 processTSeries 和 processMParallel 方法,processTSeries 方法重点在于根据串联电路中各设备情况准确计算串联电路的电阻值,processMParallel 方法则是更新并联电路的总电阻值,通过分析并联电路中各串联支路的状态等来重新计算并联电路的总电阻。
基于电路状态的设备参数计算与调整部分
计算各设备输入电压(循环遍历串联电路设备并调用 updateInvBasedOnType 方法):
获取最后一个串联电路(last_SeriesElement),以初始输入电压(设定为 220.01)开始,循环遍历该串联电路中的设备,根据设备类型调用 updateInvBasedOnType 方法来更新输入电压,不同类型设备有不同的电压更新逻辑将更新后的电压赋值给设备的 inVoltage 属性,并作为下一个设备的输入电压继续传递计算,模拟电压在串联电路中各设备间的传递和变化过程。
分情况处理设备参数(根据最后串联电路状态分 if-else 分支):
最后串联电路导通(seriesState == 1)的情况:
首先调用 handleEquipmentInSEquipmentMapWhenOne 方法处理最后一个导通串联电路中的各设备,接着遍历其他非导通的串联电路,调用 handleEquipmentInSEquipmentMapWhenNotOne 方法根据不同设备类型结合电阻等因素计算输入电压等属性;最后处理并联电路,通过 handleParallelEquipment 方法,根据并联电路中串联支路的状态进一步处理各支路内的设备属性。
最后串联电路断路(seriesState!= 1)的情况:
同样先对最后一个串联电路按断路情况调用 handleEquipmentInSEquipmentMapWhenNotOne 方法处理其内部设备属性,然后遍历其他串联电路,若其也断路则同样调用 handleEquipmentInSEquipmentMapWhenNotOne 方法结合各自的电阻和输入电压情况计算设备参数,对于并联电路,根据其当前输入电压情况调用 handleParallelEquipment 方法处理内部设备属性,通过这样分情况的复杂处理,全面更新各个设备在当前电路整体状态下的相关参数(如输入电压、转速、光照强度等)。
计算光照强度并调整相关设备(循环遍历串联电路并调用相关方法):
通过循环遍历所有串联电路(SeriesList),调用 calculateLightFromSeries 方法计算从串联电路中的设备获取的光照强度总和,将各发光设备的光照强度值累加起来。然后再根据这个光照强度总和,调用 adjustSEquipmentBasedOnLight 方法调整窗帘设备的相关属性,模拟了光照强度对窗帘控制的关联逻辑。
LinkedList<Equipment> equipment_List = new LinkedList<>(EquipmentMap.values());
Series last_SeriesElement = SeriesList.getLast();
double inputVoltage = 220.01;
double light = 0;
for (String equipment_Id : last_SeriesElement.seriesEquipMap.keySet()) {
Equipment new_quipment = last_SeriesElement.seriesEquipMap.get(equipment_Id);
updateInvBasedOnType(new_quipment, inputVoltage);inputVoltage = new_quipment.inVoltage;}
if (last_SeriesElement.seriesState == 1) {
handleEquipmentInSEquipmentMapWhenOne(last_SeriesElement.seriesEquipMap);
for (int index = 0; index < SeriesList.size() - 1; index++) {
Series series = SeriesList.get(index);
if (series.seriesState!= 1) {
handleEquipmentInSEquipmentMapWhenNotOne(series.seriesEquipMap, series.resistanceValue, 0);
}}
for (Parallel parallel : ParallelList) {
handleParallelEquipment(parallel.paraEquipMap, 0);}} else {
handleEquipmentInSEquipmentMapWhenNotOne(last_SeriesElement.seriesEquipMap, last_SeriesElement.resistanceValue, inputVoltage);
for (int index = 0; index < SeriesList.size() - 1; index++) {
Series series = SeriesList.get(index);inputVoltage = series.inVoltage;
if (series.seriesState!= 1) {
handleEquipmentInSEquipmentMapWhenNotOne(series.seriesEquipMap, series.resistanceValue, inputVoltage);
}}
for (Parallel parallel : ParallelList) {inputVoltage = parallel.inVoltage;
handleParallelEquipment(parallel.paraEquipMap, inputVoltage);
}}
设备信息输出部分
设备列表排序:
将存储所有设备的 EquipmentMap 的值转换为列表(equipment_List),使用自定义的比较器(comparator 类实现的 Comparator 接口)对设备列表进行排序,排序规则按照规定的设备类型顺序(orderChars 中定义的顺序)先排序,同类型设备再根据设备标识字符串顺序排序,确保设备按照特定、统一的顺序输出信息,方便查看和整理电路中各设备的最终状态。
逐个输出设备信息(循环调用 displayEquipmentInfo 方法):
通过循环遍历排序后的设备列表,调用 displayEquipmentInfo 方法输出每个设备的详细信息,对于普通设备直接调用其重写的 showmethod 方法按照各自格式输出(如风扇输出转速、灯光输出亮度等),对于 H 型开关设备(mutex_switch)信息输出时,通过正则表达式匹配等操作进行特殊处理(避免重复输出相同互斥开关的相同状态信息),最终按照统一格式展示各个设备在当前电路最终状态下的具体情况,完成整个电路模拟结果的展示功能,让用户清晰了解电路中各设备经过一系列操作和状态变化后的最终状态表现。
for (Series series : SeriesList) {
light += calculateLightFromSeries(series.seriesEquipMap);
}
for (Series series : SeriesList) {
adjustSEquipmentBasedOnLight(series.seriesEquipMap, light);}
equipment_List.sort(new comparator());
ArrayList<String> arraydevice = new ArrayList<>();
for (Equipment equipment1 : equipment_List) {
displayEquipmentInfo(equipment1, arraydevice);
}
3.类图时序图
改进建议
代码结构与组织方面
部分类中的方法职责有些繁杂,例如 Main 类的 main 方法过于庞大,包含了从输入处理、电路构建、控制命令执行到电路状态更新以及设备信息输出等众多不同阶段的逻辑,可以将这些逻辑进一步拆分到多个更细粒度的方法中,使得每个方法专注于一个明确的子任务,这样代码的可读性和可维护性会更好。
可以将电路状态更新相关的一系列操作封装成一个单独的 updateCircuitState 方法,把设备信息输出相关逻辑提取到 displayAllEquipmentInfo 方法等。
对于一些类,如 Equipment 作为基类,目前其 showmethod 方法是空实现,从设计角度看,可以考虑将其定义为抽象方法,强制要求子类必须实现各自的信息展示逻辑,这样能更明确地体现出多态性的设计意图,也避免在子类中忘记重写该方法而导致潜在的逻辑错误。
输入处理与验证方面
目前代码中对于用户从控制台输入的内容,除了简单判断是否符合特定控制命令前缀以及通过正则表达式匹配提取部分信息外,缺少更全面的异常处理机制。建议增加更多的输入验证逻辑,对不符合要求的输入给出友好的提示信息,引导用户正确输入,提高程序的健壮性。
电路状态更新逻辑方面
状态更新的效率优化:
在 Series 类的 updateSeriesState 方法中,存在多次遍历 seriesEquipMap 的情况,可以考虑优化逻辑,尽量在一次遍历中完成多个条件的判断和状态更新,例如使用合适的标志位来记录不同类型设备的相关状态,减少不必要的重复遍历操作,提高状态更新的效率。
代码复用与设计模式应用方面
重复代码提取:在多个地方存在相似的代码逻辑,例如在 handleEquipmentBasedOnTypeWhenNotOne 和 handleEquipmentInSEquipmentMapWhenOne 等方法中,对于不同设备类型都有获取设备电阻值并进行相关计算的操作,可以将这部分公共的获取电阻值以及一些通用的计算逻辑提取成独立的方法,减少代码重复,提高代码复用性,同时也便于统一修改这部分逻辑。
PTA第八次作业
设计与分析
题目分析
迭代内容:
1.增加管脚电压的显示
2.电流限制:每个元器件都有最大电流的设置,当实时电流超过最大电流时,在该电器输出信息的最后加入提示“exceeding current limit error”
3.短路检测:如果电路出现无穷大的电流造成短路,所有元器件信息不输出,仅输出提示“short circuit error”
4.并联电路中包含并联:即构成并联电路的串联电路可以包含别的并联电路。
5.二极管增加二极管元件,其电路特性为:正向导通,反向截止
6.输入输出细节方面
分析:
需要采用面向对象编程方式构建家居强电电路模拟程序,整体架构上设计多个类分别表示不同设备、电路结构及整个模拟系统,让各部分各司其职完成功能。
类设计方面,设备基类作为抽象类定义设备共有属性与抽象方法,开关类、互斥开关类、分档调速器类、连续调速器类及各类受控设备类等具体设备类继承基类实现各自特性相关逻辑并处理电流限制判断;
电路相关类中,串联电路类与并联电路类分别存储连接信息、构建电路结构及计算相关参数并处理嵌套情况,电路模拟系统主类作为入口协调各环节。
输入处理时,用标准输入流按行读取直至遇“end”标志,再依据格式判断输入类型分类处理,通过正则或分割提取设备信息创建设备对象,解析连接信息构建电路连接关系,按格式处理控制调节信息改变设备状态。
电路状态计算与模拟中,先基于初始状态与电路连接情况设置初始管脚电压并判断电流是否超限,接收调节信息后依据电路知识多次迭代更新电路状态。
输出环节先将设备排序再遍历,按规定格式输出设备信息,短路则输出特定提示。
异常处理方面,针对输入不符合格式及运行时可能出现的异常分别做好合理提示与处理,保障程序稳定。
知识点解析
继承:
运用了类的继承机制, Switch、Devide、Continue 等具体设备类都继承自抽象类 Equipment,继承了其共有的属性。Series 和 Parallel 类继承自抽象类 circuit,用于处理不同类型的电路结构。
多态:
通过抽象类中的抽象方法,不同的子类根据自身需求进行重写实现,在程序运行时根据对象的实际类型来调用相应的重写方法,体现了多态性。
封装:
各个类将自身的属性和方法进行了合理封装,通过访问修饰符控制对内部数据的访问。
LinkedHashMap:
在 Main 类以及部分设备类和电路类中使用了 LinkedHashMap,LinkedHashMap 结合了哈希表和链表的特点,既能保证快速的查找操作(基于哈希表的特性),又能维护元素插入的顺序(链表特性),方便按照添加顺序遍历设备对象,或者通过设备编号快速定位设备,在整个电路模拟过程中有效地管理设备信息。
LinkedList:
多处使用了 LinkedList,如 LinkedList
Collections.sort 与 Comparator 接口:
通过自定义的 comparator 类实现了 Comparator
异常处理:
使用try-catch对出现的特殊情况进行了判断和处理,以避免程序出现错误或不合理的结果。一定程度上保证了程序在面对异常数据或特殊情况时的稳定性和可靠性。
调试过程
1.类的定义分析
Equipment
抽象类
- 功能概述:作为所有电路设备以及电路结构的抽象基类,定义了通用的属性和部分通用的行为方法,同时在该抽象类中也包含了一些和电路连接、状态更新、参数计算以及结果展示相关的基础逻辑。
- 主要成员变量:
type
:表示设备类型的字符串。id
:设备的唯一标识符。pin
:引脚相关信息。resistance
:设备的电阻值,初始化为0。inv
、outv
、v
:分别表示设备的输入电压、输出电压以及当前电压。electricity
:设备允许通过的最大电流值。i
:设备当前通过的电流值。isreserve
:布尔值,用于标记设备是否处于某种备用等特殊状态。
- 主要方法及逻辑:
changestate1(Equipment e)
:抽象方法,用于判断当前设备相对于给定设备e
的状态是否改变。changev(circuit s)
:根据给定的电路对象s
的电阻和电压,计算当前设备的电压,同时考虑电路电阻为0或总电压过大等边界情况进行相应处理。changelinkpin(Equipment pre, circuit c)
:用于更新设备与前一个设备(pre
)在电路(c
)中的引脚连接相关的电压信息,针对Hswitch
等特殊设备以及普通设备分别有不同的处理逻辑来正确传递和更新电压值。changepin(circuit e)
:根据设备相对于电路e
的状态以及自身是否为Hswitch
等情况,更新设备的输入输出电压等引脚相关电压信息,同时对电压值进行边界处理。changereserve()
:根据设备的引脚信息来更新isreserve
状态。display(Equipment e)
:根据设备类型以及当前的各种参数情况,格式化输出设备相关的状态信息,输出内容包括设备的标识符、当前状态以及是否超出电流限制等提示信息。
Switch
类
- 功能概述:代表电路中的开关设备,用于控制电路的通断,继承了
Equipment
类的通用属性和方法,并实现了自身特有的开关切换以及状态判断逻辑。 - 主要成员变量:
state
:表示开关的当前状态,0
表示打开,1
表示关闭,初始化为0
。
- 主要方法及逻辑:
changestate()
:用于切换开关的状态,将state
的值在0
和1
之间进行切换,以此改变电路的通断情况。changestate1(Equipment e)
:重写父类方法,根据自身的state
值判断相对于给定设备的状态是否改变,若state
为0
(打开状态)则返回false
,表示电路不通;若state
为1
(关闭状态)则返回true
,表示电路连通。
Devide
类
- 功能概述:调节输出电压比例的设备,通过调整
speedlevel
属性来改变输出电压占输入电压的比例,同样继承Equipment
类并实现相关逻辑。 - 主要成员变量:
speedlevel
:表示设备的速度级别或者说电压调节级别,初始化为0
,通过相应指令可以增加或减少该值来改变输出电压情况。
- 主要方法及逻辑:
changestate1(Equipment e)
:重写父类方法,直接返回true
,意味着该设备默认总是处于一种对电路状态有影响的“连通”状态。changeoutv(double inv)
:根据当前的speedlevel
值计算并返回输出电压,不同的speedlevel
对应不同的输出电压与输入电压的比例关系,同时对边界情况进行处理。
Continue
类
- 功能概述:可调节参数并且基于参数改变输出电压的设备,通过设置
parameter
属性来决定输出电压占输入电压的比例,继承Equipment
类的相关基础逻辑。 - 主要成员变量:
parameter
:表示调节参数,初始化为0
,可通过外部指令设置其具体值,取值范围在0
到1
之间用于控制输出电压比例。
- 主要方法及逻辑:
changestate1(Equipment e)
:重写父类方法,返回true
,表明设备默认对电路状态判断来说是处于有效连通状态。setparameter(double p)
:用于设置parameter
参数的值,以便后续根据该参数计算输出电压。changeoutv(double inv)
:依据当前的parameter
值以及输入电压inv
来计算输出电压,当parameter
不在有效范围(小于0或大于1)时返回0
,同时考虑输入电压为0的情况进行相应处理。
Bright
类
- 功能概述:代表某种发光设备(比如灯泡之类),其根据输入电压的不同来设置自身的发光亮度,继承了
Equipment
类的通用行为并实现了亮度计算逻辑。 - 主要成员变量:
light
:表示设备当前的发光亮度,初始化为0
,后续根据输入电压按照特定规则进行更新。
- 主要方法及逻辑:
changestate1(Equipment e)
:重写父类方法,返回true
,意味着默认在电路状态判断中该设备处于有效工作状态。setlight(double inv)
:根据输入电压inv
按照一定的分段函数逻辑来设置light
(发光亮度)的值,不同的电压区间对应不同的亮度计算方式,如电压在0
到9
之间亮度为0
,在9
到220
之间按照特定公式计算,大于220
则亮度设为固定值200
等。
Sun
类
- 功能概述:太阳光源设备,根据输入电压来确定发光强度,继承
Equipment
类并实现自身的光强设置逻辑。 - 主要成员变量:
light
:表示设备当前的光照强度,初始化为0
,会随输入电压变化而更新。
- 主要方法及逻辑:
changestate1(Equipment e)
:重写父类方法,返回true
,表明在电路状态判断层面设备默认处于有效作用状态。setlight(double v)
:依据输入电压v
来设置光照强度,若电压为0
则光照强度设为0
,否则设为固定值180
,体现了该设备特定的光强与电压关系。
Fan
类
- 功能概述:代表风扇类设备,根据输入电压来调节风扇的旋转速度,继承
Equipment
类并实现了转速计算逻辑。 - 主要成员变量:
rotate
:表示风扇当前的旋转速度,初始化为0
,会根据输入电压按照特定规则更新。
- 主要方法及逻辑:
changestate1(Equipment e)
:重写父类方法,返回true
,说明在电路状态判断角度设备默认处于正常运行可影响电路状态的情况。setrotate(double inv)
:基于输入电压inv
按照不同的电压区间来计算并设置风扇的旋转速度,例如电压小于80
时转速设为0
,在80
到150
、大于150
等不同区间有不同的计算方式来确定具体转速值。
AFan
类
- 功能概述:风扇设备,同样依据输入电压来确定旋转速度,继承
Equipment
类并实现自身独特的转速计算逻辑。 - 主要成员变量:
rotate
:代表风扇的旋转速度,初始化为0
,根据输入电压更新其值。
- 主要方法及逻辑:
changestate1(Equipment e)
:重写父类方法,返回true
,意味着在电路状态考量中该设备默认处于有效运行状态。setrotate(double inv)
:按照多个不同的电压区间来分别设置风扇的旋转速度,实现了基于不同电压范围对应不同挡位转速的逻辑。
Hswitch
类
- 功能概述:切换连接不同的串联电路等情况
- 主要成员变量:
s2v
、s3v
:分别表示连接到该开关的两个不同串联电路对应的电压值,初始化为0
,在电路运行过程中会动态更新。state
:表示开关当前的状态,字符串类型,初始化为"closed"
,通过change
方法可切换为"turned on"
等状态,同时关联着开关连接的不同电路情况。s2
、s3
:分别指向连接到该开关引脚2
和3
的Series
(串联电路)对象,初始为null
,用于表示电路连接关系。ison2
:布尔值,用于标记当前开关是否连接到引脚2
对应的电路,初始为false
,通过change
方法进行切换,并且关联着电阻等属性的变化。
- 主要方法及逻辑:
change()
:用于切换开关的连接状态,改变ison2
的值,从而决定后续电路连接以及电阻等属性的改变情况。changer()
:根据ison2
的值来更新开关自身的电阻,同时更新state
状态字符串("turned on"
或"closed"
)以反映当前连接情况。changestate1(Equipment e)
:重写父类方法,首先判断给定设备e
是否为circuit
类型,若不是则返回false
;然后根据ison2
的值来判断当前开关连接的是引脚2
还是3
对应的电路,并与给定设备e
进行比较,返回对应的连接状态判断结果。
S
类
- 功能概述:窗帘设备,其开合程度根据输入电压以及整个电路中的光照强度等因素来调节,继承
Equipment
类并实现自身特定的状态更新逻辑。 - 主要成员变量:
openness
:表示设备当前的开合程度。
- 主要方法及逻辑:
changestate1(Equipment e)
:重写父类方法,返回true
,表明在电路状态判断中该设备默认处于有效可调节状态。changeopenness(double light)
:根据输入电压(通过inv
属性体现)以及给定的光照强度light
值按照不同的条件区间来设置设备的开合程度。
P
类
- 功能概述:通断设备,根据自身的
state
值以及相关条件来影响电路的连通情况,继承Equipment
类并实现对应逻辑。 - 主要成员变量:
state
:表示设备当前的通断状态,1
表示导通,0
表示截止,初始化为1
。
- 主要方法及逻辑:
changestate1(Equipment e)
:重写父类方法,根据自身的state
值判断相对于给定设备的状态是否改变,若state
为1
(导通状态)则返回true
,表示电路连通;若state
为0
(截止状态)则返回false
,表示电路断开。
circuit
抽象类
- 主要成员变量:
emap1
:类型为LinkedHashMap <String,Equipment>
,用于存储该电路结构中所包含的各个设备,以设备编号作为键,对应的设备对象作为值,方便管理和查找电路内的设备,不同的电路子类(串联、并联)会基于实际情况往里面添加和操作设备。eList
:LinkedList<Equipment>
类型,通过getlist
方法进行初始化,用于存放电路内部设备的列表形式,便于在一些需要顺序遍历设备的逻辑中使用,比如在更新电路中各设备的连接引脚、电压等信息时按顺序操作设备。
- 主要方法及逻辑:
getlist()
:实例化eList
,将emap1
中存储的设备值提取出来转换为列表形式,方便后续遍历等操作使用。changev()
:抽象方法,要求子类必须实现,目的是实现电路整体电压相关的更新逻辑,不同的电路结构(串联、并联)有各自不同的电压计算和分配规则。changei()
:抽象方法,同样需子类重写,用于更新电路中的电流情况,例如根据电路的总电阻、总电压以及各支路设备的电阻等信息来计算每个设备上通过的电流值,不同电路类型有不同的电流计算和分配策略。changepin()
:抽象方法,由子类实现,用于处理电路中各设备引脚相关的电压信息更新,考虑电路连接关系、设备先后顺序以及断路、通路等情况来正确设置各设备的输入输出电压等引脚电压值,确保电路中电压信息的正确传递和更新。
Series
类
- 功能概述:串联电路结构。
- 主要方法及逻辑:
changestate()
:用于更新串联电路的整体状态,遍历电路内包含的各个设备(通过emap1
),根据不同类型设备(如开关Switch
、特殊开关Hswitch
、其他电路结构相关设备等)的状态来综合判断整个串联电路的通断情况。changev()
:重写circuit
类的抽象方法,遍历电路中的所有设备,调用每个设备的changev
方法(从Equipment
类继承而来的用于单个设备根据所在电路电压、电阻情况计算自身电压的方法)来更新各设备电压。changei()
:重写circuit
类的抽象方法,首先判断整个串联电路相对于自身(通过changestate1
方法判断是否通路)是否处于连通状态,如果不连通则直接返回不进行后续操作;若连通,则遍历电路内各设备,对于状态满足条件(通过changestate1
判断)的设备,将其电流值设为整个串联电路的电流值(i
),并且如果设备本身也是电路结构则递归调用其changei
方法来更新内部电路设备的电流,以此实现串联电路中电流处处相等以及在嵌套电路中正确传递电流值的逻辑。changepin()
:重写circuit
类的抽象方法,先通过getlist
方法获取设备列表,然后从第一个设备开始处理引脚相关电压信息,对于特殊的Hswitch
等设备根据其是否处于备用状态以及连接关系来设置对应的电压值,接着依次更新每个设备与前一个设备之间的连接引脚电压(通过changelinkpin
方法)以及自身引脚电压(通过changepin
方法),对于电路结构类型的设备递归调用其changepin
方法;之后还要倒着再扫描一遍设备列表,处理断路开关后面设备的电势更新情况(通过R_changepin
等相关方法),以此保证在各种复杂的串联电路连接以及通断情况下设备引脚电压信息的准确更新。changestate1(Equipment e)
:重写circuit
类方法,通过获取电路内所有设备列表并遍历,调用每个设备的changestate1
方法判断是否相对于当前电路(自身)处于连通状态,只要有一个设备不连通则整个电路相对于给定设备e
就判定为不连通(返回false
),只有所有设备都连通才返回true
,以此判断整个串联电路与外部给定设备的连通性状态。
Parallel
类
- 功能概述:并联电路结构。
- 主要方法及逻辑:
changer()
:用于更新并联电路的总电阻值,首先判断整个并联电路相对于自身是否连通,如果不连通则将总电阻设为极大值表示断路;若连通,则遍历电路内各设备(通过emap1
),对于状态满足条件(通过changestate1
判断)的设备,根据其电阻值计算总电阻(按照并联电阻计算公式,通过累加各支路电阻的倒数来计算总电阻的倒数,最后再取倒数得到总电阻),以此实现并联电路总电阻的正确更新,同时考虑边界情况(如某个支路电阻为0则总电阻为0等)进行相应处理。changev()
:重写circuit
类的抽象方法,遍历电路中的各设备,对于不是断路状态(通过((Series)e).state
判断,这里假设串联电路的state
能反映其断路情况,可能需要结合实际更严谨判断)的设备,将其电压设为并联电路的总电压(v
)。changei()
:重写circuit
类的抽象方法,遍历电路内各设备,根据并联电路总电阻以及各设备自身电阻情况来计算每个设备上通过的电流值,对于总电阻极小时,根据设备自身电阻进一步判断分配电流。changepin()
:重写circuit
类的抽象方法,遍历电路中的所有设备,将每个设备的输入电压(inv
)和输出电压(outv
)都设为并联电路的总输入输出电压。changestate1(Equipment e)
:重写circuit
类方法,通过获取电路内所有设备列表并遍历,调用每个设备的changestate1
方法判断是否相对于当前电路(自身)处于连通状态,只要有一个设备连通则整个电路相对于给定设备e
就判定为连通(返回true
)。
comparator
类
- 功能概述:用于定义设备列表排序的规则,实现了
Comparator
接口的compare
方法。 - 主要方法及逻辑:
compare(Equipment o1, Equipment o2)
:实现接口方法,首先获取两个设备对象(o1
和o2
)编号字符串的首字符,然后查找其在预定义的设备类型顺序列表(order
)中的索引位置,按照这个索引位置来比较先后顺序,如果索引不同则根据索引大小返回差值确定顺序;如果索引相同(即设备类型首字母相同),则按照设备编号字符串的字典序(通过compareTo
方法)来进一步比较确定顺序,最终实现整个列表按照设定规则的排序。
2.重点代码代码具体分析
1.变量与数据结构初始化及输入循环部分
首先定义了几个关键的数据结构:
emap是一个LinkedHashMap,用于存储整个电路系统中所有的设备,以设备的编号作为键,对应的Equipment及其子类的实例对象作为值,方便后续通过编号快速查找相应设备进行各种操作和属性获取。
controllist是一个LinkedList,用来存放用户输入的控制指令相关的字符串,比如控制开关状态改变、设备参数调节等指令,后续会对其进行遍历解析来执行相应操作。
binglist和chuanlist分别是存储并联电路和串联电路的列表,便于集中管理不同结构的电路,按顺序对它们进行状态更新、参数计算等操作。
通过Scanner从标准输入获取用户输入的内容,利用while (true)循环持续读取,直到接收到用户输入的end字符串才结束输入过程,随后根据输入内容的不同格式来进行针对性的处理。
2. 处理以#T开头的输入(串联电路配置)
对于以#T开头的输入,先是通过正则表达式Pattern和Matcher的配合,解析出串联电路的名称(sname),然后创建对应的Series类实例s,代表这个串联电路结构,它将作为后续添加串联电路中各个设备的容器。
接着再用另一个正则表达式解析输入字符串中设备相关的信息,循环获取每个设备的编号(id)和引脚(pin)。根据id的格式,利用if-else if结构创建不同类型的设备实例,并做以下操作:
如果设备编号在当前串联电路实例s的emap1中不存在,且设备实例e不为null,就将该设备添加到s的emap1中,实现串联电路内部设备的关联存储。
同时,如果设备编号在总的设备映射表emap中不存在且设备实例e不为null,也会将该设备添加到emap中,保证所有设备都能在全局的设备集合里被管理。
最后,将串联电路实例s放入emap中,方便后续查找整个电路系统中的所有电路结构,并且把s添加到chuanlist中,便于集中处理所有串联电路的相关操作,如状态更新、参数计算等。
3. 处理以#M开头的输入(并联电路配置)
同样使用正则表达式先解析出并联电路的名称(pname),然后创建Parallel类实例p来表示这个并联电路结构。
接着从输入字符串中提取出包含在该并联电路中的设备编号信息,经过处理后得到设备编号数组pa,再通过循环将这些编号对应的设备从全局设备映射表emap中取出,并添加到当前并联电路实例p的emap1中,以此构建并联电路与它所包含设备之间的关联。
最后,把并联电路实例p放入emap中方便后续查找,同时添加到binglist里,便于后续统一对所有并联电路进行如电阻计算、状态更新等相关操作。
else if(entry.startsWith("#M")) {
String regex2 = "#(M\\d+):(.*)";String regex22 = "[\\[\\]]";String regex222 = "\\s+";String replacement = "";
Pattern pattern2 = Pattern.compile(regex2);
Matcher matcher2 = pattern2.matcher(entry);
String strAfterColon = entry.substring(entry.indexOf(":") + 1, length - 1);
String processedStr = strAfterColon.replaceAll(regex22, replacement);
String[] splitArray = processedStr.split(regex222);
if (matcher2.find())
{
String parallel_name = matcher2.group(1);
Parallel shunt = new Parallel(parallel_name);
EquipmentIDList.add(parallel_name);
ParallelList.add(shunt);
Collections.addAll(shunt.paraEquipIdList, splitArray);
EquipmentMap.put(parallel_name, shunt);
}
}
}
4. 处理控制指令部分
遍历controllist这个存放控制指令的列表,根据指令开头的标识进行不同操作:
对于以#K开头的指令,先提取出具体的设备编号(去除#K前缀),然后在emap中查找对应的设备,如果存在且是Switch类型的开关设备,就调用其changestate方法来改变开关的状态,实现对开关的控制操作。
以#F开头的指令用于控制Devide类型的调速设备,通过解析指令字符串获取相关位置索引,判断是要增加还是减少调速等级(speedlevel),进而对相应设备的调速等级进行相应的调整操作。
对于其他不同开头(#L、#H等)的指令,也都有各自对应的设备类型和具体操作逻辑,分别实现对诸如连续调节设备、特殊开关等不同设备的参数调节或状态改变等控制功能。
for (String device_id : newEquipmentIDList) {
EquipmentUtils.processTSeries(EquipmentMap, device_id);
EquipmentUtils.processMParallel(EquipmentMap, device_id);
}
for (Series value : SeriesList) {
value.updateSeriesState();
}
for (String device_id : newEquipmentIDList) {
EquipmentHandler.processTSeries(EquipmentMap, device_id);
EquipmentHandler.processMParallel(EquipmentMap, device_id);
}
5. 电路状态更新与参数计算部分
首先遍历chuanlist,调用每个串联电路实例的changestate方法,该方法内部会依据串联电路中各个设备来综合判断整个串联电路目前是处于通路还是断路状态,完成串联电路整体状态的更新。
接着遍历emap的键,针对不同编号格式进行不同处理:
对于以T开头的编号,进一步遍历该串联电路实例下包含的各个设备编号,根据设备类型进行相应操作。例如,若是Hswitch类型的特殊开关设备,就调用其changer方法来更新相关属性;如果是Parallel类型的并联电路设备或者嵌套的Series类型串联电路设备,同样会调用各自对应的方法来更新其内部状态、参数,并且还会进行诸如判断电路连通性、累加电阻等复杂的逻辑操作,来确保整个电路系统的状态和参数能正确更新。
对于以M开头的编号,调用对应的并联电路实例的changer方法,来根据其包含的各个支路设备的状态和电阻等情况更新该并联电路的总电阻等参数,实现并联电路相关参数的计算与更新。
for (Parallel parallel : ParallelList) {
handleParallelEquipment(parallel.paraEquipMap, 0);
}
} else {
handleEquipmentInSEquipmentMapWhenNotOne(last_SeriesElement.seriesEquipMap, last_SeriesElement.resistanceValue, inputVoltage);
for (int index = 0; index < SeriesList.size() - 1; index++) {
Series series = SeriesList.get(index);
inputVoltage = series.inVoltage;
if (series.seriesState!= 1) {
handleEquipmentInSEquipmentMapWhenNotOne(series.seriesEquipMap, series.resistanceValue, inputVoltage);
}
}
for (Parallel parallel : ParallelList) {
inputVoltage = parallel.inVoltage;
handleParallelEquipment(parallel.paraEquipMap, inputVoltage);
}
}
6.输出相关处理部分
先创建elist,它包含了从emap中提取出的所有设备实例,方便后续统一处理输出相关的信息展示工作。
然后从chuanlist中获取最后一个串联电路实例e,通过遍历其包含的设备,依据设备类型来确定初始电压v,并将该电压值赋给电路实例e的相关属性,为后续计算设备的电流、状态等参数做准备。
接着遍历elist中的所有设备,根据设备类型分别调用对应的方法来设置设备在当前电路状态下的相关显示属性,这些属性值的计算依赖于前面更新好的电压等参数,实现根据电路运行结果来展示各个设备的具体状态表现。
之后会计算一个总的光照相关参数light,通过遍历串联电路中特定设备来累加它们的光照值,再利用这个光照值去更新S类型设备的相关属性,进一步完善设备状态信息的展示。
从elist中获取最后一个设备实例t,判断其电阻是否为0,若是则表示出现短路情况,输出相应错误提示信息并结束程序,避免后续不合理的计算和展示。
最后,利用自定义的comparator对elist进行排序,按照特定的设备类型顺序以及设备编号的字典序规则整理设备顺序,再通过循环调用每个设备实例的display方法,输出每个设备详细的状态信息,完整地展示整个电路系统最终的运行结果。
3.类图时序图
改进建议
类职责单一性优化:
部分类的职责有些繁杂,例如Equipment类中包含了众多不同设备类型的显示逻辑以及多种与状态、引脚、电阻等相关的操作方法,可考虑将这些逻辑根据功能进一步拆分到不同的类或者抽象出专门的接口来实现,使每个类的职责更加单一明确,便于理解和维护。
像Series和Parallel类中,除了实现自身电路结构相关的核心逻辑外,还包含了对各种具体设备(如开关、特殊开关等)状态改变的处理逻辑,可尝试将设备状态相关逻辑提取到对应的设备类中,让电路类专注于电路结构相关的操作,如电压、电流、电阻的计算以及引脚处理等。
减少类之间的耦合度:
当前代码中各个类之间的依赖关系比较复杂,例如在很多地方通过强制类型转换(如((Series)emap1.get(key))这种形式)来调用不同类的方法,这使得代码的可扩展性和可维护性变差。可以通过定义接口或者抽象方法,让类之间通过更规范的接口进行交互,降低耦合程度。例如,对于不同电路结构(串联、并联)和设备,定义统一的获取电阻、判断连通性等接口方法,这样在处理电路组合和设备交互时会更加灵活和清晰。
边界情况和异常处理完善:
在很多涉及数值计算和对象操作的地方,对于可能出现的边界情况考虑不足,例如在电阻计算中除以接近 0 的数值时可能导致异常,在设置设备状态、电压、电流等参数时没有对非法输入值进行充分的校验和处理,可添加更多的边界条件判断以及合理的异常抛出或者默认值设置,提高程序的健壮性。
对于一些可能出现null引用的地方(比如通过编号获取设备对象时,如果编号不存在就返回null),后续使用这些对象时没有进行充分的null检查,容易引发NullPointerException,需要完善相关的null判断逻辑,避免程序崩溃。
踩坑心得
这段代码实现了一个电路模拟系统,能够对包含多种不同类型设备组成的串联、并联电路进行配置、控制以及展示电路状态等操作。整体架构上通过不同的类来表示各种设备、电路结构,同时有工具类辅助处理电路中设备间的关联构建、状态更新以及结果输出等逻辑。
问题描述:
在代码多处使用正则表达式进行输入字符串的解析,例如在识别不同设备编号格式、提取电路配置信息等场景中。一开始,正则表达式的编写容易出现匹配不准确或者遗漏某些边界情况的问题,比如在解析以#T或#M开头的输入来构建串联、并联电路时,若正则表达式模式写得不够严谨,可能无法正确提取出电路名称、所包含的设备编号等关键信息,导致后续电路结构初始化出错。
解决思路:
仔细分析每种输入格式的特点以及需要提取的内容,利用在线正则表达式测试工具对编写好的表达式进行反复测试,针对不同的测试用例输入(涵盖各种正常和边界情况)进行验证,确保能准确匹配到期望的字符串部分,同时配合Matcher类的方法正确提取相应分组信息。
问题描述:
代码中使用了多个LinkedList、LinkedHashMap等集合来存储设备信息、电路结构以及控制指令等内容。在对这些集合进行元素添加、删除、遍历操作时,容易出现数据不一致或者重复添加、遗漏处理某些元素的情况。比如在从输入构建电路结构时,向Series或Parallel类中的设备列表(如seriesEquipIdList、paraEquipIdList等)添加设备编号,如果没有做好去重处理或者在后续依据这些编号从全局设备映射表(EquipmentMap)获取设备实例并关联时出现遗漏,就会导致电路结构信息不准确,进而影响后续的状态更新和结果输出。
解决思路:在涉及集合操作的关键位置添加清晰的注释说明操作目的以及期望的集合状态变化情况,对于添加元素的操作尽量先判断元素是否已存在(可以通过自定义的equals方法或者直接调用集合自带的contains等方法来检查)避免重复添加,对于遍历操作确保遍历的完整性以及对每个元素处理的正确性,必要时添加日志输出查看集合在关键操作前后的实际内容,方便排查问题。另外,抽取一些通用的集合操作方法(如deduplicateStringList方法用于去重)来保证操作的规范性和复用性。
总结
这段两次pta作业构建了一个较为复杂且完整的电路模拟系统,可以接收不同格式的输入来构建串联、并联电路结构,到依据各种控制指令去灵活调整众多类型设备的状态,再到细致地计算电路各部分参数、更新设备相关属性,最终全面展示整个电路系统各设备的详细状态,整个流程覆盖了电路模拟的核心环节。我花费了巨大的心思去构思和实现各个细节逻辑。可惜还是没有满分。不过,至少努力过!
学期总结
学完java这门课程,这大三上这一学期也将落下帷幕。java让我体验到了新的一种代码思维,面向对象的设计思维。java面向对象编程的特性,使得java可移植性高,使用广泛。而C语言因面向过程,易于学习。
回顾人之一生,许多人苦苦追求过程。最近的我,步入人生十字路口,总有一个心结让我追求功与名的与大家相似的过程。因为它们太像,我常常在写Java代码时便不由自主的把C的语法强行带入,于是反复发问,为什么它会没有指针,为什么它不可以多继承,为什么它不支持运算符重载……
可是,哪有这么多为什么?转念一想,我为什么苦苦追求这过程,我是我自己,我要面向我自己编程,做独一无二的我!