OOP课程第三次blog—23201408—钱文浩
本次blog,针对第7~8次大作业题目集进行总结,目的在于总结不足以及这8次大作业以来我的所有收获与感慨。本次blog分为以下几个部分。
一、前言:
(1)第7次和第8次大作业难度很大,需要考虑的情况很复杂多样,如果你的代码还是只能处理一个不关注顺序的电路或者不关注电源、接地的电路,那么第8次题目集对你就非常棘手了,第8次题目集的迭代内容引入了对每个设备两段引脚的电压的输出,这就要求代码中相应的List必须要严格按照电路中的设备顺序记录这个电路的信息,否则即使每个电器的电压正确、工作时的状态正确,其两端的引脚的电压输出不正确,那么所有测试点也一样报错,一切努力化为泡影。那为什么一定要考虑电源和接地呢?因为接电源的引脚的电压一定为220V,接地的引脚电压一定为0V,无论中间的过程是怎么样的,这个结论永远不会改变,所以这是模拟整个电路首先要考虑的。我之前的代码只存入了电路的各个设备的信息,并没有存入电源和接地的数据,导致很多电器的引脚明明都接地了电压却不为0,这是严重违背物理规律的。
(2)这两次题目集,无一例外都考察了多种类间关系的设计以及对系统的模拟能力,要设计出一个符合实际、简洁高效的模拟系统需要设计者具有很强的建模能力、阅读理解能力(需要模拟的信息都是以文字形式呈现,三次大作业的题面阅读量都很大,需要设计者精确捕捉到有用信息)。并且这里的“符合实际”,要求严格按照物理定义上的电路记录输入的信息,不能想当然的打乱电器顺序,不管是串联电路电阻(等于各个用电器电阻之和)还是并联电路电阻(等于各个支路的电阻倒数之和的倒数),都要严格按照物理定律和题目要求,是无限大就是无限大,如二极管的21接法,可变电阻就是可变电阻,如互斥开关两个引脚的电阻不同。如果整个电路中有一个电阻因为粗心设置错误或者因为设计有缺陷计算错误,那么不只是用电器的电压和其工作状态会出现问题,用电器两端引脚的电压也会出现问题,这是非常致命的。
二、设计与分析:
(1)
第7次相比第6次在逻辑层面的设计思路很简单,因为只加入了总路上会有多个并联电路的情况,虽然总体提升的难度还不算小,但是相比第7到第8次题目集的难度提升程度还是算比较小的,毕竟不涉及嵌套的引脚的电压输出。
(2) 本次bolg重点对第8次大作业进行讲解。
以下是上次的类图:

新增以下两个类

二极管本身属于特殊的电器,其实它的性质应该类比开关的性质,所以让他继承toControl也就是控制设备类,因为二极管21接时其实就相当于是断路,开关断开。calPinVoltage不继承任何类,单独用来计算各个设备的引脚电压,这是因为计算引脚电压时整个电路的信息必须要被完整地记录,如果电路有任何一部分信息没有被记录是不能开始计算引脚电压的,所以这一部分必须要单独设置一个类,而且这样符合单一职责原则。
三、踩坑心得:
(1)理解题意。在解题过程中,首先要确保准确理解题意。许多学生在初次接触题目时,往往因为急于动手编写代码而忽略了对题目要求的全面理解。建议在开始编程前,花时间仔细阅读题目描述,弄清输入输出要求和功能实现细节。如果题目描述中有不清楚的地方,可以与同学讨论或向教师请教,避免因为理解错误而导致代码偏离题目要求。
(2)逐步实现。面对复杂题目时,可以采用分步实现的策略。将复杂问题拆解为若干个子问题,逐步解决。先实现核心功能,然后逐步添加扩展功能和优化代码。例如,在实现一个银行账户管理系统时,可以先实现账户的创建和基本操作,然后再添加账户的转账功能、账户明细查询功能等。这样可以逐步推进,减少因为一次性实现过多功能而带来的调试困难。
(3)注意边界情况。在编写代码时,除了考虑常规输入,还需要注意边界情况和异常情况的处理。例如,数组越界、空指针异常、输入数据格式错误等。建议在测试代码时,多设计一些边界情况的测试用例,确保代码的健壮性。例如这里一定要注意数组的范围,不要越界。
for (int i = 0; i < paraOfCer; i++)// 几个并联电路
{
for (int j = 0; j < serCircuit[allCerNumber].getPara().get(i).getSeri().size(); j++)// 并联电路中几个串联电路
{
if (serCircuit[allCerNumber].getPara().get(i).getSeri().get(j).getVoltage() != 0) {
break;
}
if (j == (serCircuit[allCerNumber].getPara().get(i).getSeri().size() - 1)) {
serCircuit[allCerNumber].setVoltage(0);
}
}
}
(4)在读入输入信息时一定不要想当然地认为输入的电路中的所有编号(包括电路编号和设备编号)一定是一个个位数。一定要提前做好准备,最好是用正则表达式或者分割字符串来获取所有编号信息。这样一定能完整地获取编号的各个位。因为题目最后要求的排序是字典排序,这意味着必然有编号为多位数的测试点存在,所以一定不能只获取某个字符把它作为编号。如下,采用分割字符串的方法。
for(int i=0;i<information.size();i++)
{
str=information.get(i);
tmp1=str.split(":");
num=tmp1[0].substring(2);
number=Integer.parseInt(num);//电路编号
if(str.charAt(5)=='I')//分析串联支路
{
tmp2=tmp1[1].split("\\s"); //tmp2是[[IN,K1-1],[K1-2,B1-1]等等 ]
for(int j=1;j<tmp2.length;j++)
{
tmp3=tmp2[j].split("-"); //tmp3是[p2,2]]或[[H1,2]等等
if(tmp3[0].charAt(0)=='K')
{
if(tmp3[1].charAt(0)=='1')
{
Equippment equ = new Equippment();
num2=tmp3[0].substring(1);
namestr = "K";
number2 = Integer.parseInt(num2);
equ.setNum(num2);
equ.setNamestr(namestr);
equ.setOrder(1800);
equ.setMaxCurrent(20);
serCircuit[number].addEqu(equ);
}
}
if(tmp3[0].charAt(0)=='F')
{
if(tmp3[1].charAt(0)=='1')
{
Equippment equ = new Equippment();
num2=tmp3[0].substring(1);
namestr = "F";
number2 = Integer.parseInt(num2);
equ.setNum(num2);
equ.setNamestr(namestr);
equ.setOrder(1700);
equ.setMaxCurrent(18);
serCircuit[number].addEqu(equ);
}
}
(5)调试技巧。调试是编程过程中不可避免的一部分,掌握有效的调试技巧可以大大提高解决问题的效率。以下是一些常用的调试方法:
- 打印调试:通过在关键代码处添加打印语句,输出变量值和执行流程,帮助定位问题。
- 使用调试器:使用集成开发环境(IDE)提供的调试工具(如Eclipse、IntelliJ IDEA等),设置断点、单步执行、查看变量值等,深入分析代码执行过程。
- 代码审查:对照题目要求,逐行检查代码逻辑,确保代码实现与题目要求一致。
(6)关于字典排序。虽然题目集里没有明说排序必须要求使用字典排序,但是很多要求排序的复杂的题目其实基本上都要求字典排序,毕竟序号可能是多位数,从小到大排序意义确实不大。这个问题也是我和其他同学交流得知的,所以就算没有遇到问题也要多和同学交流,同学一句不经意间的提醒可能会帮助你发现程序的盲点、多过很多个测试点。这里我就修改了compareTo方法,用来比较类型和进行字典排序。
public int compareTo(Equippment o) {
// TODO Auto-generated method stub
String str1=o.getNum();
String str2=this.getNum();
int number1=Integer.parseInt(str1);
int number2=Integer.parseInt(str2);
if(str1.length()==str2.length()){
return (o.order-this.order)+(number2-number1);//长度相同,升序
}
else{
for(int i=0;i<Math.min(str1.length(),str2.length());i++){
if(str1.charAt(i)!=str2.charAt(i)){
return (str1.charAt(i)-str2.charAt(i))+(o.order-this.order);
}
}
return (str1.length()-str2.length())+(o.order-this.order);
}
}
(5)
四、改进建议:
(1)优化算法
在完成题目后,可以思考是否有更高效的算法。例如,可以从时间复杂度和空间复杂度两个方面入手,寻找更优的解法。对于复杂度较高的算法,可以考虑使用更高效的数据结构或算法优化技巧。例如,将暴力解法优化为动态规划、使用更高效的排序算法等。
(2)提高代码可读性
可读性是衡量代码质量的重要标准之一。为了提高代码的可读性,可以采取以下措施:
- 合理命名:使用有意义的变量名、方法名和类名,便于理解代码功能。
- 注释清晰:在关键代码处添加注释,说明代码的功能和实现思路,帮助他人(包括未来的自己)理解代码。
- 简洁明了:避免过于复杂的嵌套结构和冗余代码,保持代码简洁明了。
(3) 重构代码
重构是指在不改变代码外部行为的前提下,对代码进行优化和调整。重构可以提高代码的可维护性和扩展性。常见的重构方法包括:
- 方法抽取:将重复的代码片段提取为独立的方法,减少代码重复,提高代码复用性。
- 类分解:将职责过于复杂的类分解为多个职责单一的类,遵循单一职责原则,提高代码的模块化程度。
(4)进行性能测试。在优化代码性能时,可以进行性能测试,评估代码的执行效率。常见的性能测试方法包括:运行时间测试:记录代码的运行时间,比较不同算法或优化前。虽然这几次题目集并没有着重要求代码的执行效率和时间复杂度,只限制了最大行数,但是这并不意味着我们可以完全不顾及时间复杂度。一个优秀的程序的时间复杂度几乎一定是很低的,如果你的程序冗杂而效率低下,那很有可能是某一模块的处理方式有问题,这不仅仅关乎效率,而且往往关乎对错,就像纸上答题一样,如果思路是一团乱麻就算最后结果是对的但过程可能有很多漏洞,再迭代一次这些问题就会暴露出来。这无疑会大大降低代码的可复用性,这是迭代的大忌。
五、总结:
(1)这两次大作业,我最大的收获就是对一个物理系统的模拟、设计能力提升。对于一个物理系统,基础的设计知识比如SRP、MVC是不够用的,充分理解物理系统的原理是必要条件。程序不仅要符合逻辑,也要严格符合物理规律。
(2)纵观我第8次大作业的完成情况,确实很不理想。虽然这和题目集的难度大大提升有关,但我认为最重要的是我对电路系统模拟的思路有漏洞,我没有在一开始模拟的时候考虑到要严格按照电路中的顺序存入信息,并且还要有效地处理这些信息。
(4)大胆设计,小心编程。一开始编码时,我因为一直瞻前顾后、担心设计出问题而耽误了不少时间,结果最后设计还是有不少瑕疵,所以我认为可能没有最好的设计、不要过度犹豫。但是大胆设计不意味着草率设计,草率设计没有意义甚至还会有副作用,我们每次遇到的现实问题都不可能相同,但是把握好基础的设计原则,即使大胆设计也不会太大的问题。
(5)虽然题目集的练习告一段落,但不代表面向对象的学习结束了。生活中诸如购物系统、支付系统有很多类似电路系统的实例可以用java去模拟,在课余要针对其他实例进行训练,多思考多总结。

浙公网安备 33010602011771号