一、前言
本次Blog为第四~六次PTA的总结,历时三周,主要考察我们对继承的掌握情况,通过完成这几次PTA,我对java的继承的使用有了了解,这三次大作业较之前的大作业难度进一步提升,尤其是第六次大作业,由于之前生病了,我没有投入几天的精力解决这次PTA,在最后一天没能及时完成,这是一次惨痛的教训。
第四次大作业共三题,第一次要求使用继承,第一题是校园角色类设计-1,第二题是设计一个学生类和它的一个子类——本科生类,两道题都需要使用继承,第三题是对上一次答题判题程序的升级,本次PTA加入填空题和多选题,需要我们继承原有问题类,建立新的三个子类:一般问题类、填空题类、多选题类,这次题目的难度和工作量都较大。
第五次的大作业共三题,第一题是家居强电电路模拟程序-1,作为接下来家居强电电路模拟程序的引入难度相对较小,电路中只有一条串联电路,但是考虑到后续大作业难度的增加,我们需要设计好各个父类、子类,应对后续的改进,这也是我第六次大作业的教训之一,其他两题主要考验我们对集合的使用。
第六次大作业共一道题,即家居强电电路模拟程序-2,因为身体原因,我没有能够花费足够的时间完成这道题目,仅仅完成了一个框架,直到作业结束我才完成这道题,这件事也给了我一个教训,一定要留足时间,题目添加了一种电器,电路中存在并联电路,要考虑更多情况,整体难度大于第五次大作业。
二、设计与分析
1、 第四次大作业
第四次大作业的第三题是对前三次大作业的一个整合,由于加入了多种题型的题目,所以我使用一个题目父类和三个题目子类来创建不同的题目,并且我吸取上次的教训,使用Map类型来替换上一次设计的单个类,这次共有十一个类:主类、题目父类、一般题类、填空题类、多选题类、试卷类、答卷类、答题类、信息处理类、删除题目类、学生类。
概要设计:
题目父类:包含两个Map类型私有数据域,分别是题目内容和题目答案,设置了有参构建方法,还存在获取题目内容和答案的方法以及一个判断题目是否存在的方法,作为题目整个类的父类,包含了题目的公共特征。
protected Map<Integer,String> agent=new HashMap<>();//题目内容protected Map<Integer,String> standardanswer=new HashMap<>();//题目答案
一般题类:继承了题目父类的变量和方法,加入了一个add方法用于添加题目,设置了有参构建方法,负责存储所有一般题的题目内容和答案,设置了一个判断题目对错的方法isright。
填空题类:继承了题目父类的变量和方法,加入了一个add方法用于添加题目,设置了有参构建方法,负责存储所有填空题的题目内容和答案,设置了一个判断题目对错的方法isright,一个私有方法getansweragents,将所有题目答案分割开,以便一一比较。
多选题类:继承了题目父类的变量和方法,加入了一个add方法用于添加题目,设置了有参构建方法,负责存储所有多选题的题目内容和答案,设置了一个判断题目对错的方法isright,一个私有方法getansweragents,将所有题目答案分割开,以便一一比较。
试卷类:包含一个map类型私有数据域,根据题号存储分数,设置了有参构建方法,有一个判断满分是否为100的方法checktotalscore,一个根据输入题号获取分数的方法getscore,负责存储单张试卷的题号及分数信息。
答卷类:包含题号、学号、答题内容三个私有数据域,以及它们的获取方法,设置了有参构建方法,负责存储单张答卷的全部信息。
删除题目类:包含删除题目题号列表一个私有数据域以及它的获取方法,设置了有参构建方法,设置了一个判断是否已删除的方法isDelete和添加删除题号的add方法,负责存储所有要删除的题目信息。
学生类:包含学号-姓名一个Map类型私有数据域以及它的获取方法,设置了有参构建方法,设置了一个根据输入学号输出名字的方法getname和判断学生是否存在的方法isexist,负责存储学生信息。
答题类:存储以上所有信息,并进行整合,设置了有参构建方法,设置了一个方法show用于输出结果,一个私有方法processAnswersheet用于处理答卷进行判题和分数计算。
信息处理类:没有数据域,设置了题目信息(包含多选题、一般题、填空题)、试卷信息、答案信息、学生信息、删除题目信息五种字符串的信息处理方法,不符合格式返回null。
主类:初始化十个类的变量,调用各个类的方法,并负责进行对输入格式的判定,实现题目需求。
类图如下:
详细设计
1、题目父类
设置了有参构建方法,作为题目整个类的父类,包含了题目的公共特征,本身并不参与。
2、一般题类
继承了题目父类的变量和方法,加入了一个add方法用于添加题目,设置了有参构建方法,负责存储所有一般题的题目内容和答案,设置了一个判断题目对错的方法isright。
3、填空题类
继承了题目父类的变量和方法,设置了有参构建方法,负责存储所有填空题的题目内容和答案,设置了一个判断题目对错的方法isright,一个私有方法getansweragents,将所有题目答案分割开,getansweragents直接在isright中调用,将对答案的信息处理放到类中,分担答题类压力。
具体实现如图:
4、多选题类:与填空题类似,设置了有参构建方法,负责存储所有填空题的题目内容和答案,设置了一个判断题目对错的方法isright,一个私有方法getansweragents,将所有题目答案分割开,getansweragents直接在isright中调用,将对答案的信息处理放到类中,分担答题类压力。
具体实现如图:
5、试卷类
设置了有参构建方法,负责存储单张试卷的题号及分数信息,设置了一个判断试卷是否满分为100的方法。
具体实现如图:
6、答卷类:负责存储单张答卷的全部信息,可以获取相关信息,具体信息处理交给答题类。
7、删除题目类
负责存储所有要删除的题目信息,有一个判断题目是否被删除的方法isDelete。
具体实现如图:
8、学生类
负责存储所有学生信息,有一个判断学生是否存在的方法isexist。
9、答题类:存储以上所有信息,并进行整合,我采用按照试卷寻找答卷的方式进行判题,在对题目的判断中,我首先按照题目要求按照顺序进行判定,这次我在各个类中分别设置了一部分判断对错的方法,进而降低了答题类中代码的书写长度。
具体代码如图:
10、信息处理类
没有数据域,设置了题目信息(包含多选题、一般题、填空题)、试卷信息、答案信息、学生信息、删除题目信息五种字符串的信息处理方法,不符合格式返回null。
11、主类
初始化十个类的变量,调用各个类的方法,并负责进行对输入格式的判定,实现题目需求。
2、 第五次大作业
第五次大作业是家居强电电路模拟程序-1,作为后续家居强电电路模拟程序序列的引入,它的难度较低,主要难点在于分辨各个电器,处理电路情况,要考虑电路开关断开等情况,还要考虑精度问题,所以我使用double型变量存储一些如亮度之类的信息,本次大作业我设计十二个类:电器父类、开关子类、分档调速器子类、连续调速器子类、日光灯子类、白炽灯子类、吊扇子类、落地扇子类、串联电路子类、并联电路子类、主类。
概要设计:
电器父类:包含电器类型、序号、电压、电阻等信息,以及题目的获取方法,以及一个打印信息方法printfdata,作为父类,有全部子类的公共特征。
开关子类:继承父类,本身包含一个开关状态的私有数据域,及获取方法,还有更改开关状态的方法changeisclosed,以及一个获取输出电压的方法getoutput,还有一个打印当前状态的方法printfdata,负责存储开关信息和更改状态。
分档调速器子类:继承父类,本身包含挡数状态、记录各挡位输出电压数组两个私有数据域及获取方法,以及一个获取输出电压的方法getoutput,两个调整挡位的函数Upshift和Downshift,还有一个打印当前状态的方法printfdata,负责存储分档调速器信息和更改状态。
连续调速器子类:继承父类,本身包含挡数状态一个私有数据域,以及一个获取输出电压的方法getoutput,一个设置挡位的函数setGears,还有一个打印当前状态的方法printfdata,负责存储连续调速器信息和更改状态。
日光灯子类:继承父类,本身包含光照强度一个私有数据域,以及一个获取输出光强的方法getLight,还有一个打印当前状态的方法printfdata。
白炽灯子类:继承父类,本身包含光照强度一个私有数据域,以及一个获取输出光强的方法getLight,还有一个打印当前状态的方法printfdata。
吊扇子类:继承父类,本身包含风扇转速一个私有数据域,以及一个获取输出转速的方法getLever,还有一个打印当前状态的方法printfdata。
串联电路类:包含电器类的列表和电器类型、序号、输入电压、输出电压五个私有数据域,一个show方法展示全部电器状态。
主类:初始化类的变量,对输入信息进行处理,将数据输入串联电路类进行集中处理。
类图如下:
详细设计
1、电器父类
设置了有参构建方法,作为电器整个类的父类,包含了题目的公共特征,由于在串联电路中,每个电器归属于电器类不同的子类,因此我们用电器类的LinkedList将各个设备统一存储。
2、开关子类
继承父类,,更改开关状态的方法changeisclosed,调用一次给开关状态取反;一个获取输出电压的方法getoutput,当开关处于闭合状态,输出电压等于输入电压,当开关处于张开状态,输出电压等于0;一个打印当前状态的方法printfdata。
3、分档调速器子类
继承父类,一个获取输出电压的方法getoutput,输出电压根据挡位进行调整,使用方法Upshift和Downshift修改挡位,还有一个打印当前状态的方法printfdata。
4、连续调速器子类
继承父类,一个获取输出电压的方法getoutput,输出电压根据设置的倍数进行调整,一个设置挡位的函数setGears,还有一个打印当前状态的方法printfdata。
5、日光灯子类
继承父类,一个获取输出光强的方法getLight,光强值根据题意按照电压变化,还有一个打印当前状态的方法printfdata。
6、白炽灯子类
继承父类,一个获取输出光强的方法getLight,光强值根据题意按照电压变化还有一个打印当前状态的方法printfdata。
7、吊扇子类
继承父类,一个获取输出转速的方法getLever,转速值根据题意按照电压变化,还有一个打印当前状态的方法printfdata。
8、串联电路类
负责处理电路,首先检索电路上的所有控制设备是否处于断开状态,在根据电路顺序,设置设备电压,最后输出整个串联电路的状态。
部分代码如图:
9、主类
初始化类的变量,对输入信息进行处理,将数据输入串联电路类进行集中处理,通过两个函数进行处理,creat负责根据输入的设备创建设备,processcommand负责处理接收的命令,根据命令改变电路状态,最后通过连接各类实现题目要求。
部分代码如图:
3、 第六次大作业
第六次大作业是家居强电电路模拟程序-2,作为后续家居强电电路模拟程序序列的第二次作业,它的难度和工作量都有了显著提升,这次电路中有一个新设备:落地扇,并且还要考虑并联电路,而且加入电阻的设定,主要难点在于要考虑并联电路加入的情况,通过电阻计算电压,整体复杂度上升,需要花费更多时间去完成,我原本一天写完的想法最终破灭了,我设计了12个类:电器父类、开关子类、分档调速器子类、连续调速器子类、日光灯子类、白炽灯子类、吊扇子类、落地扇子类、串联电路子类、并联电路子类、干路子类、主类。将串联电路和并联电路当作设备看待,同时加入串联电路的子类干路类。
概要设计:
电器父类:包含电器类型、序号、电压、电阻等信息,以及它们的获取方法和设置方法,以及一个打印信息方法printfdata,作为父类,有全部子类的公共特征。
开关子类:继承父类,本身包含一个开关状态的私有数据域,及获取方法,还有更改开关状态的方法changeisclosed,以及一个获取开关状态的方法getisclosed,还有一个打印当前状态的方法printfdata,负责存储开关信息和更改状态。
分档调速器子类:继承父类,本身包含挡数状态、记录各挡位输出电压数组两个私有数据域及获取方法,以及一个获取输出电压的方法getV,两个调整挡位的函数Upshift和Downshift,还有一个打印当前状态的方法printfdata,负责存储分档调速器信息和更改状态。
连续调速器子类:继承父类,本身包含挡数状态一个私有数据域,以及一个获取输出电压的方法getoutput,一个设置挡位的函数setGears,还有一个打印当前状态的方法printfdata,负责存储连续调速器信息和更改状态。
日光灯子类:继承父类,本身包含光照强度一个私有数据域,以及一个获取输出光强的方法getLight,还有一个打印当前状态的方法printfdata。
白炽灯子类:继承父类,本身包含光照强度一个私有数据域,以及一个获取输出光强的方法getLight,还有一个打印当前状态的方法printfdata。
落地扇子类:继承父类,本身包含风扇转速一个私有数据域,以及一个获取输出转速的方法getLever,还有一个打印当前状态的方法printfdata。
吊扇子类:继承父类,本身包含风扇转速一个私有数据域,以及一个获取输出转速的方法getLever,还有一个打印当前状态的方法printfdata。
串联电路子类:继承父类,本身包含电器类的列表私有数据域,一个adddevice方法添加设备,一个totalresistance方法计算串联电路的总电阻,一个isclosed方法判断该串联电路是否断开,一个show方法展示全部电器状态,一个deal方法根据输入电压对串联电路的各个设备按照电阻分配电压。
并联电路子类:继承父类,本身包含电器类的列表私有数据域,一个adddevice方法添加设备,一个totalresistance方法计算串联电路的总电阻,一个isclosed方法判断该串联电路是否断开,一个show方法展示全部电器状态,一个deal方法根据输入电压对并联电路的各个设备按照电阻分配电压。
干路子类:继承串联电路类,有一个自己的方法show,对设备进行处理,并进行信息展示。
主类:初始化类的变量,对输入信息进行处理,将数据输入串联电路类进行集中处理。
类图如下:
详细设计
1、电器父类
设置了有参构建方法,作为电器整个类的父类,包含了题目的公共特征,由于在串联电路中,每个电器归属于电器类不同的子类,因此我们用电器类的LinkedList将干路中非并联电路的各个设备统一存储。
2、开关子类
继承父类,,更改开关状态的方法changeisclosed,调用一次给开关状态取反;一个获取开关状态的方法getisclosed;一个打印当前状态的方法printfdata。
3、分档调速器子类
继承父类,一个获取输出电压的方法getoutput,输出电压根据挡位进行调整,使用方法Upshift和Downshift修改挡位,还有一个打印当前状态的方法printfdata。
4、连续调速器子类
继承父类,一个获取输出电压的方法getoutput,输出电压根据设置的倍数进行调整,一个设置挡位的函数setGears,还有一个打印当前状态的方法printfdata。
5、日光灯子类
继承父类,一个获取输出光强的方法getLight,光强值根据题意按照电压变化,还有一个打印当前状态的方法printfdata。
6、白炽灯子类
继承父类,一个获取输出光强的方法getLight,光强值根据题意按照电压变化还有一个打印当前状态的方法printfdata。
7、吊扇子类
继承父类,一个获取输出转速的方法getLever,转速值根据题意按照电压变化,还有一个打印当前状态的方法printfdata。
8、落地扇子类
继承父类,一个获取输出转速的方法getLever,转速值根据题意按照电压变化,还有一个打印当前状态的方法printfdata。
9、串联电路类
deal负责处理串联电路,首先检索电路上的所有控制设备是否处于断开状态,在根据电路顺序,设置设备电压,根据每个通过totalresistance计算总电阻并输出,方便在更大的电路中计算获取电压。
部分代码如图:
10、并联电路子类
deal负责处理串联电路,首先检索电路上的所有控制设备是否处于断开状态,在根据电路顺序,设置设备电压,根据每个通过totalresistance计算总电阻并输出,方便在更大的电路中计算获取电压,与串联电路不同,在处理并联电路,要考虑某一支路短路或断路的情况。
部分代码如图:
11、干路子类
负责整合所有电路,在对各个设备进行处理后,输出各设备状态。
12、主类
初始化类的变量,对输入信息进行处理,将数据输入串联电路类进行集中处理,通过两个函数进行处理,createSeries负责根据输入的设备创建串联电路;createParallel负责创建并联电路;processcommand负责处理接收的命令,根据命令改变电路状态,最后通过连接各类实现题目要求。
部分代码如图:
三、采坑心得
1、 第四次大作业
这次大作业是对前三次大作业的一个升级,加入了填空题、多选题等多种题型,对于填空题和填空题,要考虑答案输入不全或输入顺序颠倒的情况,设置不同的评分,这一次大作业我大量使用Map类型变量,通过这种方法可以一一对应存储不同信息。在使用Map是要注意,如果使用new HashMap<>(),hashmap获取的集合不是按照键的顺序排列,为此我在获取集合后还要进行排序,如果想要节省代码量,可以直接使用TreeMap。
2、第五次大作业
本次大作业作为家庭强电电路的模拟本身比较简单,需要注意输出结果要求为整数部分而不是四舍五入后的结果,在比较时要注意精度问题,最后使用double型存储某些数值,在进行整个电路的处理时,首先要考虑控制设备是否处于断开状态。
3、 第六次大作业
本次大作业加入并联电路,工作量较大,为此应该花费更多时间来解决。难点主要在并联电路的处理上,对于并联电路,要考虑某一条支路短路或断路的情况,这几次代码都没有涉及引脚,但考虑到后续实际,应该将对引脚的考量加入,同时控制设备、被控设备、电路设备应该做好区分,我这次没有区分,导致处理起来很复杂。
四、改进建议
1、首先还是代码编写注释问题,我这次虽然有了一些注释,但是在某些具体逻辑方面注释不足,导致重新编码变得困难,为了便于理解,还是应该添加详细注释。
2、对于继承的使用方面,尤其是第五次、第六次大作业,可以使用一些抽象类、接口来代替一些子类,我并没有用到抽象类和接口,因此在许多地方重复设置,提高了编码难度。
3、各个类的职责要明确,要尽量符合单一职责原则,负责会导致单个类过于复杂,我这一次也没有做到这一点,导致在完成第六次大作业吃了不少苦头。
4、家庭强电电路中存在一些边界值,需要我们考虑精度问题,因此在编码时要考虑精度问题进行误差监测,我没有很好的解决这一问题,后续要加入更多修改。
五、总结
这三次大作业使用了大量继承方面的知识,提高三次编码,我自己设计了许多父类和子类,这让我对继承和多态有了实际应用上的了解。
第四次大作业,我仅仅是设计并使用了几个问题的子类,但在实际使用时,我只是把子类当作普通的类来使用,在初始化各个题目时,我也只是使用各个子类来进行定义,此时我对子类的定义仅限于它可以使用父类的方法,但不知道它与父类的区别。
第五次大作业,我对继承有了了解,父类具备子类的公共特征,但是每个子类也有各自更具体地属性和职责,当我们要处理一系列并不相同但具备公共特征的一个集合时,我们可以先用父类统一存储,正在具体处理时加一分别。比如,我使用LinkedList< Electric >来存储一条串联电路上的所有设备,但在具体使用时,用instanceof分辨它们属于哪一种设备。
部分代码如图:
第六次大作业,我沿用了第五次大作业的设计,同时在将串联电路、并联电路视为设备的基础上进行进一步细分,将干路视为串联电路的子类,进一步分配职责。同时我也意识到了可以使用接口,实现类继承多个接口,降低编码难度,做出职责的进一步分配。
三次大作业,使我对继承有了深入地实践,对于我,现在需要去进一步学习抽象类和接口的使用,提高了解抽象类和接口,进一步做好职责的分配,降低编码难度。
我建议课上老师可以邀请一些代码得分高的同学针对某些问题,介绍自己的思路,可以起到一个参考的作用。