面向对象课程第一单元博客总结
整体总结
本单元的三次作业,是在“进行表达式括号展开并可尽量化简”的目标下,进行三轮迭代开发。其中总结各次需求主要递进如下:
多项式->带三角函数、自定义函数、求和函数的表达式,函数因子自变量有限制,不允许多层嵌套->函数因子自变量无限制,允许多层嵌套。
通过该单元训练,我对面向对象的思维方式有了初步体会。对于架构设计在迭代开发中的作用也有了直观感受。
个人架构分析
整体采用建立表达树的思路。先对字符串做一些简单化简,利用栈转化成后缀表达式,建立表达式树。求解过程即为自下向上更新的过程。
所有节点均继承自Node,子类通过重写update()方法实现不同种类运算。而父类Node中定义了统一的属性——表达式,左右子节点;以及公共方法update()。
类图描述
最终第三次作业的UML图如下。
优点
自认为结构清晰,各类职责明确,函数接口丰富、功能完备,鲁棒性和可扩展性较好。
缺点
显然结构复杂。代码较长,难以阅读。潜在问题是可能由于遗忘而在多个方法重复实现了同样功能,造成代码冗余。(又或许已然出现了...)
度量分析
使用IDEA的MetricsReload插件做度量分析,查看类和方法度量。截取方法度量总数据展示:
较高的复杂性总合或许可以显示,我上一项的自我评价是正确的——确实是总体结构复杂。
又,各函数平均复杂性尚可或许可以显示,我上一项的自我评价依然是正确的——确实是函数接口功能清晰完备。
综合来看也许显示了我“知道错了下次还敢”的死不悔改缺点。
通过度量发现,高复杂度的主要贡献基本是三角优化相关。这也给我以启示,说 “计算机科学中的设计思想处处是tradeoff的体现” 确实不假——这里自然也是一种设计复杂程度和优化性能指标的tradeoff。
对于较复杂的化简,我此次是“觉得自己能做就做了”,所幸没有翻车,下次设计时或许可以有意识地加入考虑。
个人bug总结
本人出现的唯一一次bug是第二次作业对求和函数没有考虑s/e为负数的情况。
这种未考虑全面造成的bug其实很有可能发生。但它带给我的反思是自己的测试显然做的不够充分。彼时我仍仅依赖于手工构造样例做测试,这只能找出 “写错了” 的bug,而不能找出 “想错了” 的bug——本就没考虑到这里有负数的情况,又怎么会构造出这种样例呢?
这次失误直接导致我第三次作业果断花点时间写自己的自动测试程序。
互测体会
三次互测我均未被发现bug。每次都有发现他人bug。
第一次互测,我几乎完整阅读了同房所有人的代码,逻辑上发现漏洞后精准hack。
这一过程耗时较大。特别对于架构设计思路和自己差异较大的程序,快速理解并确认是否有漏洞有一定困难。但确实是一个练习阅读代码和学习他人架构的机会。
第二次互测我建立在自己开发和与他人交流的基础上,直接尝试能想到的“边界数据”或化简中易出现的错误。
较第一次效率有所提高,也发现自己对于边界数据的构造思路确实没有一些同学活跃,需要锻炼。
第三次我保持了第二次的“边界数据+易错点”测试思路,又加入使用自己的评测机测试。
效率再次得到提高。不过也发现了自己评测机的不完善之处,对于一些形式上的bug未能发现。
学习心得
从第一次拿到作业时一时无从下手,到第二次看到新加要求后几乎重构,再到第三次的修改-测试全流程有条不紊进行,我对于 锻炼工程能力 的内涵有了初步理解。
首先一点感受是,完成作业不止是面向评测机编程的应付差事,花些时间和头脑做有效的工作以提升自己代码水平、确保交付作业的质量是值得的。
懒于在最初的架构上动脑很可能带来的是之后繁重的debug过程,懒于在测试上动脑很可能带来的是空耗的无效测试工夫和最终不得不再做的修复。
以及还是要能够站到一个higher level去审视自己当下在做什么。
就从最底层的完成作业来说,在给一个类加入每个新函数时,要明白这个类被赋予的职责,这个函数的具体功能;我们为什么创建了这个函数,它符合我们的设计初衷吗。或者更上层一点——在做每一项工作时,要明白它的目的是什么,我们目前走到了哪里,我们在做对这个目的有意义的事吗。 偶尔做点思考,也有助于保持冷静平和,减少焦躁抓瞎。
以前看一位故事创作者谈论人物塑造:她偏向于对短篇采用情节引导,而对长篇采用主人公引导。作为计算机系学生,对于工程能力的培养和应用,在我们的学业和职业生涯上很可能将是长篇叙事,那么主人公保持清醒、保有主观能动性将是有益的,且不要仅看到某一次作业如何,aiming high吧。
共勉。