面向对象程序设计期中与中三次作业的总结
前言
这篇博客是完成《面向对象程序设计》(java)课程的中三次PTA作业后的总结。主要内容有:
- 最后一次的答题判题程序和前两次家庭电路模拟程序的分析。
- 面向对象程序设计思想的一些学习总结
- 学习到的一些调试方法
题目分析
最后一次答题判题程序的分析
说实话已经过去三周了,有点忘了,现在看一下之前的作答其实并不是很好。这道题的题目要求是按照五种可能会打乱的,不同的输入去创建或所有类的对象。这个题目需要正确地解析和处理不规则的输入数据,并且结合类之间的关系(Exam, Question, Student, AnswerSheet)完成答题判分的过程。我是采用了AnswerSheet类、Question类和Student类三个使用的类和一个Main类(用于程序逻辑)。类的依赖如下图
数据结构上我设计了四个类,并且用集合类来存储信息。题目信息 (Question) 存储在 Map<Integer, Question> 中,其中题目的编号为键,题目对象为值。试卷信息 (Exam) 存储在 Map<Integer, Exam> 中,试卷编号为键,试卷对象为值。学生信息 (Student) 存储在 Map<String, Student> 中,学生的学号为键,学生对象为值。答卷信息 (AnswerSheet) 存储在 List
对于输入的解析,我根据每一行的内容判断输入类型(题目信息、试卷信息、学生信息、答卷信息、删除题目信息),添加到后续的数据结构里。
运行时,每个题目都会被装成一个Question对象,然后存入questions表。试卷的内容用Exam类来表示,试卷中包含了多个题目及其分值。这些试卷信息存储在exams表。学生的基本信息输入后存储到 students 映射表中。每个学生的答题情况解析后存储在 AnswerSheet 中,最终进行判断与评分,有问题的可以后续处理。在解析过程中,如果某一输入无法匹配预定格式,记录格式错误信息。
最后一次的答题判题程序还需需要使用继承,这样可以提升类的多样性(但是我其实不太懂怎么用在这里?)
家庭电路模拟程序分析
家庭电路模拟程序需要完成模拟家庭电路中的各种设备的连接和调节。
设备包括:控制设备(开关、调速器)和受控设备(如灯具、风扇)。模拟电路的连接关系及电压、电位差对设备状态的影响。电路中包含多个控制设备、受控设备以及它们之间的连接。控制设备调整受控设备的状态(如灯的亮度、风扇的转速)。电路连接信息提供了设备之间的电气连接关系。电源(VCC)提供220V电压,接地(GND)为0V电压。
那对于这种设备的模拟,比较合适的面向对象的设计的方法是继承。继承关系体现了面向对象的多态性,不同的设备可以有不同的行为和属性。最基本的类有两个:
- 设备类包含设备的基本属性,如设备名、输入引脚、输出引脚、状态等。
- 引脚类包含连接信息。
设备类和引脚类采用组合模式,设备类内含有输入和输出引脚的变量(应该没有三个脚的)。设备的行为(如开关、调速器等)是由这些引脚的电位差来决定的。
第一次家庭电路模拟程序
第一次家庭电路模拟程序不含有并联电路,只有一条线路,所有元件串联。我的类图如下(带有方法的话太复杂了,先放个不带方法的)
所有器件的基类都是electricDevice。继承出三个:控制器Controller、风扇Fan、灯light的类。electricDevice会有inJunction和outJunction两个变量,用于存储junction类的对象。junction类就是一个公共的交点,可以多个设备连接。设备引脚电压就是junction类中的voltage属性。使用继承来定义不同类型的设备(如控制器、风扇和灯具),并在基类electricDevice中处理设备的基础功能。设备的输入和输出通过Junction类来管理,Junction类存储了电压信息。
对于整个程序的逻辑,创建了一个circuitManagement类。它的运行逻辑为:输入字符串->分解为创建和调节命令->分别执行创建和调节命令->计算电压输出。存储方面,为了把“首字母”+“数字”转换为对象,采用了一个Map<String, electricDevice>进行映射。
控制器类上,其实核心是档位的概念:0就是关,>0就是开。分档调速器就是根据档位调整输出电压;连续调速器就是连续的分档调速器。
风扇类和灯类都是可使用的器件,具有输出值。风扇类就是速度,灯类就是亮度。灯类下还有不同的细分灯类,继承灯类并且重写了亮度计算。
运行流程:
- 程序将输入存储。
- 从存储的输入里面解析数据,根据输入的连接信息创建设备electricDevice对象并连接它们,并连接和创建junction 类用于存储和设备间的电压。
- 通过 processCommands() 方法解析并执行控制命令(例如开关设备、调节档位等)
- 通过 propagateVoltage() 方法,程序将初始电压通过电路传递到各个设备,并根据设备类型计算输出结果(如亮度、转速等)。
- 最后,outputStatus() 方法输出所有设备的当前状态。
当然,这个设计肯定有很多问题,比如他不能处理并联电路;并且在开关上没有做到统一(开关和调档是不同的计算方式)。
第二次家庭电路模拟程序
第二次家庭电路模拟程序中,需要继续构建并改进上一个程序的设计,重点是需要并联电路的支持。这次迭代我们将支持并联电路的输入。在并联电路中,多个串联电路将一起工作,电压会分布到并联电路的每个子串联电路上。所以在计算方法上需要修改很多地方。
在设计上,我们需要对现有类图进行一些调整,主要是增加对并联电路的支持。electricDevice类将继续为基类,但是要增加电路子类(因为电路Circuit类也具有输入和输出节点)。junction类用于管理电压信息,支持多个引脚的电压信息共享。在并联电路中,不同的串联电路会共享同一电压,junction类需要能处理这一点。计算方法上,舍弃原有的一路计算过去的方案,需要进行子电路的迭代。这种就是组合模式设计的思想体现作用的时候。因为整个电路可以看做一个串联电路,而内部的并联电路可以看做是串联电路的并联,因此我们可以抽象出串联电路类(维护一个存储着电路上的设备的表)和并联电路类(维护一个存储并联的串联电路的表)。
这次我的程序改进点主要是:
- 增加电路类,具有并联电路和串联电路子类。并联电路中,每个支路上的电压相同,而电流则根据支路电阻进行分配。因此需要处理并联电路的电压分配和等效电阻计算。
- 为了支持并联电路,我们采用了组合模式,将串联电路(serialCircuit)作为并联电路的子组件。串联电路维护一个存储电路对象的表。因为电路类也是继承electricDevice的,因此串联电路的存储电路对象的表也可以存储串联电路或并联电路对象,实现方便的递归式调用处理。这种层级结构使得电路模型更加灵活,可以模拟更复杂的电路拓扑结构。circuit类作为serialCircuit和parallelCircuit的父类,抽象了电路的共同特性,例如输入输出节点(junction)。并联电路类维护一个List,存储串联电路serialCircuit对象。
- 由于引入了并联电路,原有的电压计算方法需要进行修改。现在采用了一种递归的电压计算方法(VoltageCalculation),能够处理包含并联结构的复杂电路。首先计算等效电阻,然后根据电路结构和元件参数,逐级计算各节点的电压。
- 对于并联电路中的开关,switch_class类模拟了开关的断开和闭合状态,serialCircuit类在计算等效电阻时会检查电路中是否存在断开的开关,如果存在则将电路标记为断路状态,并停止后续计算。
- 连接点junction类来表示电路中的连接点,并通过connectDevices和mergeParallelJunction方法来连接不同的设备和电路。添加设备时,运行这两个方法可以合并连接点。同时因为Java的方便的内存管理,我们不太需要手动对junction对象进行释放。
采用组合模式之后,电路的电压计算和设备电压计算就可以通过递归调用实现了!
void calVoltage(){
setInVoltage(electricDevices.get(0).getInVoltage());
double all_voltage=getInVoltage()-getOutVoltage();
//按顺序和总电阻分配电压
for(electricDevice device :electricDevices){
double voltage= 0; //当前器件分压值
voltage=(getInVoltage()-getOutVoltage())*(device.getResistance()/getResistance());
device.setInVoltage(all_voltage);
all_voltage-=voltage;
if(!getDisonnected())
device.setOutVoltage(all_voltage);
// device.showVoltage(); //不是debug则关闭
if(device instanceof parallelCircuit){
((parallelCircuit) device).calVoltage();
}
}
}
}
electricDevice类和Junction类的关系如下:
electricDevice类通过injunction和outjunction两个属性与junction类关联,表示设备的输入和输出连接点。一个junction对象可以连接多个electricDevice对象,实现了多个设备的连接和电压共享。
class electricDevice {
private String name;
public junction injunction; // 输入连接点
public junction outjunction; // 输出连接点
private double I; // 电流
private double resistance;
electricDevice(String name) {
this.name = name;
injunction = new junction(this);
outjunction = new junction(this);
}
}
//接口类,连接两个电路设备的端点。只记录电压和连接,计算在其他地方
class junction {
//该端点连接的所有设备
public ArrayList<electricDevice> connectedDevices= new ArrayList<>();
private double voltage; // 该端点电压值
}
electricDevice类与它的子类关系如下:
类的依赖关系:
一些学到的调试方法分享
多存信息,方便打印
面向对象程序设计中,类很方便的可以帮我们进行资源管理。因此,我们可以把很多东西放心的存在里面。比如:我们可以给很多东西命个名。
在这次的家庭电路模拟程序第二次中,非常复杂的电路计算往往需要打印(System.out.print...)。这时候如果我们把创建对象的名字存起来,就很方便打印了。
例如:调试递归调用calVoltage()时,我需要知道计算顺序、电阻和电压,这时候对象名字就很有用。
使用IF条件控制调试信息打印
用IF语句,在某个参数为True时才打印调试信息,这样在提交时只需要修改那一个变量就可以关闭所有的调试信息打印了,剩下的就是题目信息。
后记
最近还在学习面向对象设计模式,我个人应用比较多的还是组合模式。对于其他设计模式也不熟悉,需要再涉猎多一点,多写一些程序来训练训练。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 易语言 —— 开山篇
· 实操Deepseek接入个人知识库
· Trae初体验