(1)基于度量来分析自己的程序结构
注:UML图中每个划分了的圆角矩形代表一个类或接口,箭头可代表创建、访问数据等行为。类的图形内部分为3个部分,从上到下依次是类的名称、类包含的实例变量(属性)、类实现的方法。接口图形内部只分2个部分,上方为接口名,下方为接口定义的抽象方法。箭头上的注释进一步解释了类之间的关系。
作业1:多项式运算
两处红色的问题都出自checkformat方法。该方法圈复杂度高,块嵌套深度高。原因在于自己的格式检查没有使用推荐的正则表达式,依然使用了传统的有限状态机。这就容易造成逻辑复杂、代码过长。而且,该方法完成的功能已经不局限于格式检查,而包含了信息提取的代码,同时实例化了term, poly, compute类的对象。
第一次作业初步体现了面向对象思维。主类的main方法只负责读取输入,之后创建了check类并将工作转交给check。checkformat方法则有“全能”的问题,负责了格式检查和输入提取,同时创建了term, poly, compute3个类的对象。被提取的输入会被整理为term和poly对象。最终,创建compute类下的对象,计算并输出结果。
作业2:单部傻瓜电梯
第二次作业较第一次有所改观。大部分方法的规模控制在30行以下。最长的方法为main,行数降低为120行。main方法圈复杂度较高。
第二次作业的进步之处在于使用了正则表达式进行格式检查,同时用String类下的split方法对有效输入的各部分信息进行分离提取。存在的问题是main方法承担了较多工作,一些无法通过简单检查正则表达式就能识别的错误仍需要在main方法中检查并报错。将这部分功能独立出来会简化main的逻辑。
由于课件中已经给出了参考设计,并明确要求实现5个类,因此设计思路基本遵循了课件。不足之处在于调度器dispatcher对同质请求的判断只通过了队列前后请求信息,而没有利用楼层、电梯按钮信息,并未完全符合老师的初衷。而且这种判断方法也容易导致BUG出现。
作业3:单部ALS电梯
问题比较严重的是main方法与schedule方法。main方法圈复杂度高,问题与第二次作业相似,都是把对无效请求的部分判断逻辑放入了其中。schedule方法块嵌套深度高,尽管将捎带请求的判断独立写了方法,但是针对电梯内请求的情况提炼总结不够,导致判断语句嵌套深度高。
由于楼层类没有实现预定功能,且出现BUG,本次作业没有将其实例化。对于继承、多态相关的设计要求均予以实现。为此,dispatcher的各方法做了较大的调整。
总结:
自己在总体设计上的优点是尽力按照面向对象的思维进行总体设计和编程,让各个对象管理自己的数据,减少数据传输,功能合理分散。没有出现自己互测拿到的那种一个方法分担了90%左右工作的情况。
缺点在于在拿到作业之初,一般不会进行书面的总体设计,导致经常性的代码改动,甚至推倒重来,在大规模开发中不可取。
一些细节并没有完全遵循指导书的意思,导致后续作业的工作量增加。(第二次作业的schedule方法就是一个例子)
第3次作业的楼层类属于鸡肋,没有被实例化过,这种做法尽管在同学中比较普遍,而且我也拿到过这样的代码,但是很不可取。
个别方法的判断逻辑过于复杂,判断和循环嵌套较深。具体的方法、类上文已详细指出。
(2)分析自己程序的BUG
第1周作业:
BUG1:空多项式输出{(0,0)},指导书要求只输出0
BUG2:同一个多项式的项之间不检查是否缺失和多逗号
BUG3:多项式之间运算符缺失不报错,按加法处理。
分析:
BUG主要是格式检查不仔细、输出格式与指导书不符。问题所在的类是check,方法是checkformat。
checkformat方法没有使用推荐的正则表达式,依然使用了“计算机组成”课中学习和使用过的有限状态机。实现有限状态机的代码逻辑会比较复杂,其可靠性很大程度上取决于编程者在绘制状态转移图时能否不重不漏、逻辑缜密。一旦需求变更或发现BUG,代码的修改也是一个很困难、痛苦的过程。自己设计时擅自简化了一部分逻辑,并且没有辨析哪些属于指导书硬性要求,导致产生BUG。
分类树分析:
BUG更多发生在“输入格式不符合规定”分支上,这与之前的总结相吻合。
第2周作业:
BUG1:格式检查不支持前导零、正号。
BUG2:输入楼层为0时程序crash
BUG3:时间为很大的整数时计算出错,时间越界不报错
BUG4:楼层类数组实现逻辑出错,在特定样例下crash
BUG5:同质请求判断不准确
分析:错误种类包括格式检查出错、同质请求逻辑不正确。出错类和方法包括:
类名 |
方法名 |
功能 |
strprocess |
checkformat |
输入字符串格式检查 |
floor |
所有 |
楼层号和上行、下行按钮 |
dispatcher |
check2 |
同质请求的判断 |
在设计楼层类时,不明确这个类的真正作用,设计了上下行按钮却没有将其纳入判断逻辑,而且数组的基本知识掌握不牢固,发生了数组越界异常的惨剧。于是第3周的作业干脆取缔了这个类,尽管可能与老师的软性设计要求相违背,但是客观上提高了程序的可靠性。
分类树分析:
“数字范围测试”被发现的BUG居多。主要是忽略了楼层为0的情况、对时间没有设上限。
第3周作业:
BUG1:没有实现多个同层捎带请求只开关一次门
BUG2:主请求选择错误
BUG3:同质请求判断出错
分析:BUG出现在本次作业的重难点——捎带与主请求选择上。
类名 |
方法名 |
功能 |
als |
schedule |
ALS策略调度电梯 |
als |
judge_f |
判断楼层请求能否被捎带 |
als |
jucge_e |
判断电梯请求能否被捎带 |
分类树分析:
“合法输入-正常情况测试-多个捎带”被发现的BUG居多。自己对代码逻辑擅自简化,没有严格实现指导书中关于多个请求捎带的要求。
(3)分析自己发现别人程序BUG所采用的策略
策略1:阅读理解正则表达式
对用户输入进行格式检查是这3次作业的共性问题。错误分类树在格式错误上也设置了不少的扣分点。笔者3次抽到的代码都是利用正则表达式完成的格式检查,因此要想发现对方格式检查方面的错误,检查正则表达式是最佳突破口。
策略2:明确硬性设计要求
为了强化学生对于面向对象思想的理解,每周的作业都会提出一些设计要求:第1次要求使用数组。第2次要求必须实现规定的5个类,不允许出现public的属性。第3次要求必须实现接口,重写和重载指定的方法。自己写代码时就要看清这些要求,同时拿到互测代码后也要严格检查。利用eclipse的outline窗口能够看到类、实例变量和方法,理清被测代码的设计结构,方便对设计要求的检查。
(4)心得体会
1)急于完成作业,没磨刀最后误了砍柴工
每周的作业一发布,自己就着急忙慌地打开IDE敲代码。殊不知在写程序前没有对总体开发做出明确规划的话,往往会频繁改变自己的设计,补上本应在写代码前做的功课。自己的状态经常是周末写写停停,然后在周日的下午将一切推倒重来,最终在周一的晚上完成了一个基础性的设计,通过了正常功能的测试样例。周二针对答疑群和Issue上的样例测试自己的代码,发现能修的BUG就修掉。周三则写其他课作业或望着IDE发呆。其实前几次作业的时间完全来得及,在开始开发之前把各个类的实例变量、方法想清楚,打好草稿是更值得推荐的做法。《Head First Java》甚至推荐学习者先写伪码和测试码,最后再写正常的程序代码。这也是“磨刀不误砍柴工”思想的一种体现。
2)总体设计要严格遵守指导书,不得偷工减料
“顾客就是上帝”的俗语在编程领域一样适用。指导书提出的要求,作为学生就应该坚决贯彻落实。如果偷工减料,最后吃亏的、学不到本事的还是自己。第二次作业对调度器类给出了更加详细的设计方案,包括command和schedule两个方法,而我没有实现。结果第三次要求继承上次的调度器并重写父类schedule方法的硬性要求害的我把整个类都重写了。第三次要求将可捎带请求放入集合,我改成遇见一条执行一条,最后同层只开一次门的问题未能完美解决。
3)debug只有进行时没有完成时
要问我每周三是怎样度过的,我的回答竟是如此颓废:盯着电脑屏幕度过的。一种可能是发现不了bug在哪里,想到什么测什么,或者直接从微信答疑群和Issue上借用别人的数据。一种可能是bug太难修复,自己直接就放弃了治疗。这不是一个二十出头的年轻人应该有的状态。一方面,只要提交窗口没关,就还有补救的机会。发现bug就要积极去修复,而不是让畏难情绪做了自己的主。第3次作业针对BUG在周三奋战了一天,尽管最后没提交那个版本,但是这种精神值得带到后续的开发中。另一方面,程序都有出错的可能。自我测试期间对于同一个分支节点应该打破思维定势,多换几组样例,而不是过了一组就认为绝对不存在问题。例如第二次作业楼层为0导致crash和整数超长应报错却不报的问题完全是多换几组样例就能发现的问题,却在我的眼皮子底下溜走。栽了一次跟斗后总算认识到了自测的重要意义。
4)好逸恶劳思想作祟,仓促应战
辅导员早已多次强调过这门课程的难度非同小可,但是我却一直以“大二下学期还早”的假象欺骗自己。《Head First Java》虽然早就买了,但是直到假期最后一星期才拆开塑封,直到开学前都没有跑过一个自己写的java程序。看到第一次作业的题目后,我吓得头皮发麻、直冒冷汗,因为自己已经有半年没有好好写过高级语言程序了。(计组实验写的是HDL语言)而且我发现自己一周看下来的java语法知识连应付这次作业都不够格。于是我第二天卸载了所有手机游戏,停止了一切娱乐活动,并做好了熬夜奋战的准备。“临阵磨枪,不亮也光。”我最终还是在ddl之前完成了能通过大多数正常功能样例的程序。但是这次的教训值得吸取。假如我能在假期就将自己的电脑配置好java环境、把第5、6章的程序自己写一遍、跑一遍,那么第一次作业来临时绝对会比现在沉稳很多。
5)学会使用搜索引擎
计算机专业的一大特点,在于书本不会教给你一切。《Head First Java》等参考书是为学习Java语言设计的,并不是Java百科。事实上,真正的百科不在书本里,而在网络上。想实现一个功能而又在书中找不到相关知识时,不妨搜索一下,也许有的人早就为此撰写过一篇博客。