前言

本次blog对前三次的PTA题目集面向对象程序设计进行总结,主要是对电梯调度程序的思考分析与反思。

1.关于知识点

第一次题目集的知识点主要是Java语法和电梯运行的算法(LOOK),未涉及oop的分类和类之间的关系,

到了第二次和第三次就涉及到类设计要求遵循的单一职责原则(SRP),将前面的类进行拆分。

2.关于题量

三次PTA题目集题量都不大第一次五题后面两次只有三题。

3.关于难度

题目集的前几题难度都不大,最后一题难度有所提高,考察逻辑思维。

设计与分析

第一次作业

单部电梯调度程序:

  1. 设计一个电梯类
  • 包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。
  1. 运行规则:
  • 电梯请求分为内部请求和外部请求,有乘客对电梯发起请求时,电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。
  • 采用串行处理乘客的请求方式。
  • 当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向

  • 电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。
  • 每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。
  1. 输入格式:
  • 第一行输入最小电梯楼层数。
  • 第二行输入最大电梯楼层数。
  • 从第三行开始每行输入代表一个乘客请求。
  • 电梯内乘客请求格式:<楼层数>
  • 电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
  • 当输入“end”时代表输入结束(end不区分大小写)。

我的设计

  1. 在处理数据方面:
  • 我采用三个数组的形式来储存乘客数据,一个用来储存内部楼层,另外两个分别用来储存外部请求的楼层和请求方向,并通过正则表达式判断读入数据是内部请求还是外部请求,并将有用的数据如楼层,方向提取出来放入对应的数组。
  1. 在处理电梯运行方向上:
  • 我采用目的楼层与当前楼层进行比较的方法来判断下一步的方向:如当前的内部楼层和外部楼层都小于当前楼层则方向要变为向下。

  • 对为空队列单独判断:如外部队列为空,只需要比较内部楼层与当前楼层楼层大小来判断方向。

  1. 电梯运行逻辑方面:
  • 紧紧围绕题目中优先处理同方向为核心进行判断返回目的楼层。

  • 主要逻辑是若当前队列都不为空,则比较内部楼层和外部楼层与当前楼层的大小关系,还有外部楼层方向是否与当前电梯同方向,比如,当前楼层为5楼,内部楼层为7楼,外部请求为<6,DOWN>,先判断是否与电梯同向,答案是不同向,再比较内外楼层与当前楼层关系,结果是都大于,则返回内部楼层,要是改为<6,UP>,与电梯同向,并且都大于当前楼层,外部楼层小于内部楼层优先去外部返回外部楼层。

  • 有队列为空时直接返回另一个不为空队列的楼层。

  1. 在队列处理方面:
  • 由于我使用数组来模拟实现队列,所以我用i,j,k来指向当前元素位置,通过自增运算(i++)来移动数组,模拟实现元素出队操作。
  1. 在输出结果方面:
  • 我采用两个方法进行输出,一个是正常移动时的语句,另一种是到目的楼层开关门的语句来实现对结果的输出。

我的分析

SourceMontor生成表:

  1. Aug Complexity(平均复杂度):
  • 平均分复杂度在绿色范围边缘,说明平均复杂度稍有欠缺。
  1. Max Complexity(最大复杂度):
  • 最大复杂度远远高于绿色范围,单个方法复杂度过高,最复杂的方法是Lift.Direction(),有过多的if语句嵌套导致复杂度过高。

  • 再结合平均复杂度来看,代码有头重脚轻的情况,某个方法复杂度很低如输出结果方法,有些方法复杂度很高如上面的Lift.Direction()。

  • 以后再写设计方法时应当做到适中,不让一个方法承担过多功能,做到低耦合,用对象+消息。

  1. %Comments(注释率):
  • 注释率过低只有5.3%,说明代码中没有多少注释会导致代码不易理解影响可读性。

  • 要在日后编码中养成写注释的良好习惯,这是非常重要的,因为项目不是单打独斗,而是团队合作,养成写注释的良好习惯有利于团队的交流,和程序后期维护。

  1. Methods/Class(每个类的方法数):
  • 每个类的方法数偏高。

  • 主要原因在于第一次作业要求只用一个类来完成未涉及类的单一职责,将所有方法均放在了电梯类中,导致方法数偏高。

  1. Avg Stmts/Method(每个方法的平均语句数):
  • 每个方法平均语句数量5.42,处于绿色范围边缘。
  1. Aug Depth(平均深度):
  • 平均深度2.07处于绿色范围内,说明代码平均深度比较恰当。
  1. Max Depth(最大深度):
  • 最大深度6,也处于绿色范围内,说明代码没有出现深度很大的结构。

第二次作业

单部电梯调度程序(类设计):

是第一次作业的迭代:

  1. 增加类设计要求遵循单一职责原则(SRP)。
  2. 增加非法数据处理:
  • 高于最高楼层数或低于最低楼层数,程序自动忽略此类输入,继续执行。
  • 输入时出现连续的相同请求,例如<3><3><3>或者<5,DOWN><5,DOWN>,处理方法:程序自动忽略相同的多余输入,继续执行,例如<3><3><3>过滤为<3>。

我的设计(在第一次基础上的增加与改变)

  1. 数据处理方面:
  • 将三个数组改成通过链表LinkedList实现两个队列一个内部队列一个外部请求队列,外部请求队列以乘客请求类为泛型可以做到同时储存楼层和方向。

  • 增加了对楼层的判断对于大于最高楼层或者小于最小楼层的数据过滤掉。

  • 对于重复的数据处理我放在了队列类中的add方法中通过比较链表最后一个数据是否与当前加入的数据一样,如果是则滤去,不是则加入。

  1. 类设计方面:

  • 设计了电梯类、乘客请求类、队列类以及控制类,降低耦合,实现单一职责。

  • 电梯类:包含题目中的属性,构造方法,及一些get和set方法。

  • 乘客请求类:主要为外部请求队列服务。

  • 队列类:将队列从电梯的移除形成一个单独的类主要用来储存,添加,修改数据。

  • 控制类: 将电梯与队列联系起来,起到“中介”作用,解耦合,实现整个电梯的运行逻辑如电梯的运行方向,目的楼层判断,运行过程,队列的出队操作。

  • 控制类与队列和电梯类关联,队列类依赖乘客请求类。

  1. 逻辑方面:
  • 整体逻辑没有改变。
  1. 队列处理方面:
  • 通过LinkedList来处理队列比第一次用数组处理简单许多,通过remove就可以实现出队。

  • 将出队操作设计成一个单独的方法,请求头队列楼层等于当前楼层就出队。

  • 总体上与前一次相比没有i,j,k等用来计入当前对头元素,入队和出队操作变得更简单,整体逻辑及代码清晰度显著提高。

我的分析

SourceMontor生成表:

与第一次相同:

  1. Aug Complexity(平均复杂度):
  • 与第一次一样平均分复杂度在绿色范围边缘,说明平均复杂度稍有欠缺。
  1. Max Complexity(最大复杂度):
  • 第二次主要还是沿用了第一次判断方向的方法

  • 与第一次一样最大复杂度远远高于绿色范围,单个方法复杂度过高,最复杂的方法是Controller.currentDirection,仍是判断方向上较为复杂

  1. %Comments(注释率):
  • 注释率过低只有5.4%,与第一次一样偏低。
  • 要在日后编码中养成写注释的良好习惯,这是非常重要的,因为项目不是单打独斗,而是团队合作,养成写注释的良好习惯有利于团队的交流。

与第一次不同:

  1. Methods/Class(每个类的方法数):
  • 由于做了类的拆分,相较于第一次每个类的方法数降低了很多,位于正常的范围内。

  • 主要原因在于第一次作业要求只用一个类来完成未涉及类的单一职责,将所有方法均放在了电梯类中,导致方法数偏高。

  1. Avg Stmts/Method(每个方法的平均语句数):
  • 每个方法平均语句数量为3.79比第一次5.42,大幅降低,超出绿色范围,可能与方法数增多有关。
  1. Aug Depth(平均深度):
  • 平均深度为1.81比第一次2.07更低更靠近圆心,第二次代码在平均深度方面有所提升。
  1. Max Depth(最大深度):
  • 最大深度为5比第一次6,更低更靠近圆心,在最大深度方面也有所提升,降低了复杂的结构。

第三次作业

单部电梯调度程序(类设计-迭代):

第二次作业的的迭代

  1. 乘客请求输入变动情况:
  • 外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>
  1. 对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)

    3.输入格式:
  • 电梯外乘客请求格式:<请求源楼层,请求目的楼层>,其中,请求源楼层表示乘客发起请求所在的楼层,请求目的楼层表示乘客想要到达的楼层。

我的设计

  1. 数据处理方面:
  • 将外部请求读入字符串改为读入数字。
  1. 逻辑处理方面:
  • 判断当前电梯方向的逻辑没有发生改变。
  • 对于返回目的楼层逻辑有所改动,由于输入的外部请求数据没有直接告诉是向下还是向上,要通过外部请求的目的楼层与源楼层相减来判断,如果大于0则代表向上,小于0则代表向下,并将这种判断方法,放在一个方法中方便使用。
  1. 队列处理方面:
  • 由于题目新增当外部请求出队时要将目的楼层加入到内部请求中,从而我增加了在外部请求出队前把目的楼层加入到内部请求中。
  • 同时出队要求有所改变,前两次题目,只要当前对头与当前楼层一致就出队,而这一次需要额外判断方向是否与当前电梯同方向,只有同向或者到达最高(<7>,<7,4>)或最低楼层(<2>,<2,5>),这时才会同时出队。
  1. 对象设计方面:

  • 乘客请求类有所改变因为输入发生改变,将原来的字符串类形改为整形,并且内部队列也使用这个作为泛型。

  • 类中方法有所变动,如在queue中增加专门为内部队列设计的构造方法只有一个参数(目的楼层)。

  • 控制类与队列和电梯类关联,队列类依赖乘客请求类。

我的分析

SourceMontor生成表:

  • 与前面两次基本一致,注释不够,最大复杂过大。

  • 不同的是类中方法分的太多导致每个方法平均语句过小。

  • 平均复杂度更靠近中心,相比与前面两次处于边缘,有提升。

踩坑心得

第一次作业

  • 刚开始拿到这个题目时非常的迷茫,一直在想电梯的运行逻辑到底是什么,没有理解队列,串行,优先处理同方向请求,这些词汇加到一起让我摸不清处电梯的运行逻辑,我也多次和周围的同学商讨运行逻辑,提出各自看法,我最开始认为需要考虑整个队列,这样情况就非常复杂,也很难实现,还有其他同学提出来的就近原则等等,作业从周一开始到周五我基本上都在思考运行逻辑。

  • 抛开需求谈设计就是耍流氓,这次电梯题目让我意识到研究需求的重要性,只有研究清楚明白了才能去作设计,否则设计也是白设计。

  • 在研究完队列,串行大致逻辑后,我开始写代码,提交后遇到的第一个情况就是非零返回,后来发现以end为标志结束是不区分大小写,我改成不区分大小写!s.equalsIgnoreCase("end"),其实还是没有研究清楚需求,题目明确表示end不区分大小写,我却视而不见,这是一个大问题,不能忽视每一个细节。

  • 解决完非零返回后提交显示答案错误代表逻辑还是有问题,在一遍一遍浏览自己代码中发现了一个问题,我是用三个数组来储存数据,外部请求有两个队列,在返回完目的楼层后我只对外部楼层数组进行j++(模拟出队),没有对方向数组进行++(未出队)。

  • 没有处理其中一个队列为空的情况。

  • 没有处理同时出队的情况。

  • 总共提交了一百多次也是挺煎熬的,最后还是坚持了下来。

第二次作业

  • 对于重复楼层判断逻辑错误,如果这样判断的话就是必须完全不一样(楼层和方向)才能入队,这明显是有错误的,应该是只要有一个不一样就入队。

  • 提交两次就过了。

第三次作业

  • 非零返回问题,一开始认为是处理输入数据的问题因为第一次没有区分大小写导致了非零返回,但是在后来发现是由于没有判断链表是否为空就直接使用,导致非零返回,以后在使用链表时一定要判断链表是否为空。

  • 错误判断问题,对于原楼层和目标楼层都只判断了一半,这可能是由于在上一次的代码的基础上进行迭代,并且依赖IDEA的Tad自动补全代码导致出现纰漏,以后在编写代码时不能无脑补全。

  • 返回目标楼层错误,当电梯运行方向向下时并且外部请求小于当前楼层,内部请求大于当前楼层应该返回外部的原楼层,而我错误返回外部请求的目的楼层。这也是用的IDEA的代码自动补齐。

  • 也提交了十几次。

改进建议

  • 三次作业注释率都很低,代码可读性不高,在以后编码时要养成写注释的良好习惯,一个方法承担的职责太多,太过复杂,要先学会拆分方法,做到分治,减小耦合。
  • 再作设计的时候如类,方法怎么分怎么处理,之间有什么关系,怎样减小耦合,提高代码复用性也是需要好好研究的,进行优化迭代,写出优质代码。
  • 不能无脑用IDEA的Tad自动补齐,因为IDEA的自动补齐不能很精准推断你下一步到底是什么,已经在上面吃过几次亏了,而且这种错误藏在类似代码中,寻找起来也相对困难。
  • 培养自己逻辑思维与抽象思维能力,在分析电梯的需求和实现逻辑上深有体会,每次都是在自己认为怎么怎么样,将题目细节要求抛开一边,对于方向判断和目的楼层判断也过于复杂,一味的用大量if语句,在日后编程中要降低冗长的结构,通过设计重构来实现合理的代码结构。

总结

  • 三次作业给影响最为深刻的就是对需求的分析,不要盲目的写代码,一定要把需求研究清楚才能去做设计,抛开需求谈设计就是耍流氓。
  • 学会了本阶段的核心AVA的23个模式之一类的设计要遵循单一职责原则(SRP)。
  • 逻辑思维,对于复杂问题的分析能力,与抽象思维获得提升。
  • 对于一些代码辅助工具要谨慎使用,如IDEA代码的补全。
  • 现在回首看去,电梯题目也没有多难,不要有畏难情绪,咬咬牙,坚持坚持,总会有柳暗花明的时候。
  • 通过迭代的这种方式进行非常符合实际情况,我们在作一个项目开发时,客户的需求可能发生变化,或者有些BUG被发现,要对它不断的进行维护和迭代。

南昌航空大学-软件学院-面向对象程序设计第一阶段总结性blog——钟文君24201237的更多相关文章

  1. 南昌航空大学-软件学院-22206104-段清如-JAVA第一次Blog作业

    南昌航空大学-软件学院-22206104-段清如-JAVA第一次Blog作业 前言: 这个学期才开始接触java,到现在一个多月的时间,已经差不多可以写出一些基本的简单的程序了.对比上个学期学习的C语 ...

  2. 面向对象(OO)第一阶段学习总结

    前言:对OO本阶段作业情况说明 本阶段一共完成三次作业,第一次主要是在主方法里面进行编程,也就是和之前C差不多,而随着学习的深入,慢慢了解到面向对象与面向过程的区别.作业的难度也在慢慢增大,后两次都用 ...

  3. 面向对象程序设计第三次blog

    一.前言 第六次题目集总结-- 题量:较少 难度:较高 知识点: 判断输入内容 提取输入的有效信息并进行计算 总结:题目比较难,题量较少. 第七次题目集总结-- 题量:较少 难度:一般 知识点: 输入 ...

  4. 王之泰201771010131《面向对象程序设计(java)》第一周学习总结

    王之泰201771010131<面向对象程序设计(java)>第一周学习总结 第一部分:课程准备部分 填写课程学习 平台注册账号, 平台名称 注册账号 博客园:www.cnblogs.co ...

  5. 201871010111-刘佳华《面向对象程序设计(java)》第一周学习总结

    <面向对象程序设计(java)>第一周学习总结 正文开头: 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 ...

  6. 201871010123-吴丽丽《面向对象程序设计(Java)》第一周学习总结

                                                                            201871010123-吴丽丽<面向对象程序设计 ...

  7. 王颖奇 201771010129《面向对象程序设计(java)》第一周学习总结

    <面向对象程序设计(java)>第一周学习总结 第一部分:课程准备部分 填写课程学习 平台注册账号, 平台名称 注册账号 博客园:www.cnblogs.com wangyingqi 程序 ...

  8. 王燕 201771010126《面向对象程序设计(java)》第一周学习总结

    王燕 201771010126<面向对象程序设计(java)>第一周学习总结 王燕 201771010126<面向对象程序设计(java)>第一周学习总结 第一部分:课程准备部 ...

  9. 马凯军201771010116《面向对象程序设计(java)》第一周学习总结

    马凯军201771010116<面向对象程序设计(java)>第一周学习总结 第一部分:课程准备部分 填写课程学习 平台注册账号, 平台名称 注册账号 博客园:www.cnblogs.co ...

  10. 周强201771010141《面向对象程序设计(java)》第一周学习总结

    周强201771010141<面向对象程序设计(java)>第一周学习总结 第一部分:课程准备部分 填写课程学习 平台注册账号, 平台名称 注册账号 博客园:www.cnblogs.com ...

随机推荐

  1. Week09_day05(Hbase的安装搭建)

    搭建完全分布式集群 HBase集群建立在hadoop集群基础之上,所以在搭建HBase集群之前需要把Hadoop集群搭建起来,并且要考虑二者的兼容性.现在就以5台机器为例,搭建一个简单的集群. 软件版 ...

  2. windows 配置java发布环境

    一.jdk安装 1.下载jdk安装文件 2.在"系统变量"下"新建"选项"JAVA_HOME"值为:"jdk"文件夹路径 ...

  3. API方式开发AI应用的三点总结

    1. 编程式prompt 让 AI 具备类似程序的运行逻辑.把大模型当CLR使用.与传统的角色扮演提示prompt相比,此方式所需的tokens数量更少,且输出结果的准确性更高 .示例如下: 2. 语 ...

  4. 机器学习 | 强化学习(6) | 策略梯度方法(Policy Gradient Method)

    6-策略梯度方法(Policy Gradient Method) 策略梯度概论(Introduction) 基于策略(Policy-Based) 的强化学习 对于上一节课(价值函数拟合)中采用参数\( ...

  5. mysql - 存储过程及分支语句

    存储过程是预编译好的sql语言的集合 减少编译次数,提高sql语句的重用性.但是在现阶段因为其维护困难及其他一系列的原因,有些时候并不推荐使用 创建 create procedure 存储过程的名字 ...

  6. RSA算法详解及相关数学原理解析

    RSA算法详解及相关数学原理解析 前言 ‍ 为了记录自己学习密码学的过程,也是为了便于个人应付相关课程的考核,故写此博客. 本博客总结了怎么用C++手搓一个RSA算法,以及补补欠缺的一些数学知识和可能 ...

  7. protected修饰符讲解、java中继承的特点-java se进阶 day01

    1.protected权限修饰符的介绍 之前在说权限修饰符时候,没有细说protected,今天,我们就来聊聊protected 如图,protected修饰符中,"不同包的子类" ...

  8. Random和猜数字小游戏

    1.Random:使用方式和Scanner一样 Random用于生成随机数,括号里的10就是指在10以内随机生成一个数(0~9) Random生成的随机数都是从0开头 . 提问:那该如何让Random ...

  9. 【Java】异常处理

    异常的定义 异常:在Java语言中,将程序执行中发生的不正常情况称为"异常". (开发过程中的语法错误和逻辑错误不是异常) 1. 异常的体系结构 Java程序在执行过程中所发生的异 ...

  10. FireDAC开发DataSnap应用系统【1】-快储功能

    FireDAC是吧DataSnap服务器当成API来调用,而dbExpress使用IAppServer接口. 关键点: 1.客户端调用API要回传数据,那么FireDAC把数据已Stream的格式传递 ...