Loading

BUAA-OO-Unit4暨OO总结

本单元架构设计

主要分为5大类:接口方法类,数据库类,处理器类,封装类,辅助方法类。

功能
接口方法类MyImplementation 提供用户接口,调用处理器方法
数据库类Database 输入数据归档 完成基本的初始化,如把关系比较大的UML元素组合在一起
处理器类XXHandler 用于响应类图or顺序图or状态图查询指令
封装类MyUMLXXX 方便组合需要的UML元素,提供扩展功能
辅助方法类Tarjan 完成较复杂的图计算

事实上,这个架构完全是被CheckStyle逼出来的:第一次作业,接口方法类承担了全部的归档、初始化、计算任务。第二次作业码量增加,超过了CheckStyle单类500行的限制,不得已把处理器类单独拆了出来,420行勉强通过。第三次作业又增加了9个方法,写完后主类已超700行,于是又把数据整理和初始化作为数据库拆了出来。最终形成了该架构。

全流程可描述为:数据输入 => 归档 => 初始化 => 检查R00x => 响应指令

三次作业没有经历大改或重构,作业之间保持了较好的延续性。

四个单元架构设计与OO方法理解的演进

第一单元:设计一个表达式解析器,表达式含有-+*/ 三角函数 幂函数 自定义函数 求和函数 数字 和 变元。要求在不改变输入表达式语义的前提下,展开输入表达式中所有可以展开的括号,同时尽可能的化简表达式。

  • 第一次作业由于理解不当,经历了大改,在助教的提示下,采用了递归下降算法解析表达式,为第二、第三次作业打下了较好的基础。

  • 第二次作业时间比较充裕,提前预判并兼容了第三次作业解析“嵌套因子”的需求,第三次作业就比较轻松。说明架构设计要为后续需求留下提前量。递归下降的方法,初步让我感受到继承和多态的威力。

第二单元:实现一个电梯调度系统。存在纵向普通电梯和横向环形电梯。要求用尽可能短的时间,把全体乘客送到目的地。这一单元主要对多线程及锁机制有了一定的认识。

  • 第二次作业我为了规避并发问题,设计了一个预分配的电梯调度算法。预分配的调度理论上与自由竞争的调度差别不大,但在极端情况下(先有m个乘客提出电梯请求,接着有n-1个新电梯加入系统)性能会弱n倍,这是因为新加入系统的电梯无法被利用。鉴于此,我设计了强盗电梯策略,插入电梯中。表现为当新电梯出现时,会自发的检测邻居电梯,并从邻居电梯的等待队列中抢夺乘客。实际性能与自由竞争较为接近。

  • 第三次作业由于使用了动态路径规划,当新电梯加入系统,会自动触发全体等待乘客的路径规划方法,重新进行一个预分配。因此,强盗电梯的策略不再有存在的意义。由于该策略是以方法调用的形式提供的,于是只需简单注释掉了策略方法那一行。借用OS黑书的那句话:策略与机制要分离。分离开了,调整起来就比较容易。LOOK算法更底层,可以看作我的电梯调度“机制“。如果当初把策略嵌入LOOK算法内部,那我只能带着相对简单粗暴的强盗电梯的策略做第三次作业,相对更精细复杂的动态路径规划就注定不会成功。

第三单元:根据JML规格,实现一个社交网络。要求可以处理社交网络中的群、人员、消息相关指令。

  • 这一单元难点在于阅读JML规格。架构已经由教学团队给出,只需要按规格实现代码。不得不说,这是一次感受教学团队架构设计风格的难得体验。这一单元并不直接训练我们描述规格的能力,而是从实现规格入手,训练我们理解规格的能力。依我看,以术入道,比以道驭术容易且有趣的多。

  • 第一次作业刚公布时,我曾经向舍友吐槽老师是提出需求(指导书)的客户,助教是将需求描述成规格的架构师,我们是纯纯的码农:啥也不用管,按规格实现就行了。现在看来,的确是这样。这个单元有助于我们脱离码农思维,从更高一个层级理解现实需求和代码实现的联系。

第四单元:解析UML元素,响应UML图相关的查询指令

  • 这个单元的难点在于应对CheckStyle。CheckStyle存在的意义就是倒逼我们设计出“风格优美”的代码。什么是“风格优美“?经过这个单元的训练,我的感悟是:架构层级分明,各司其职;方法完全解耦,代码语义清晰;自底向上层层封装,可维护性强。

课程收获的总结

学习了

  • 递归下降算法,电梯调度算法,多线程的并发艺术,JML规格的理解与书写,UML类图的再理解;

  • 继承、多态、封装、容器、泛型、异常……

复习了

  • 数据结构和程序设计学过的Dijkstra、并查集、Prim、拓扑排序、Tarjan。(奇怪,怎么全是图论)

体会了

  • 一切皆为对象的抽象思维,学会以对象为单位构造程序。

当然,上面的东西跟下面的比起来都不重要。

以前深受OI思维毒害,看不上方法的多层调用,觉得能用一个函数写完的简单方法,为什么要来回调用?压栈弹栈时间很宝贵的,能省就省。OO确确实实给我上了一课。照以前的想法写单元作业,要么自己写的代码,过一个星期就看不懂,根本维护不动,更别说给别人看;要么就要接受CheckStyle的制裁。从这个角度上讲,OO课教会我最重要的内容,既不是花里胡哨的算法,也不是多线程等编程机制,而是写出人能维护的代码,即语义清晰、封装良好、高度解耦、可重用、可扩展的代码。

测试的理解与实践的演进

测试的目的是为了发现实现上的bug和设计上的漏洞。

第一单元:手工构造简单表达式,大眼观察法和代入简单数值计算。

第二单元:按一定的规则随机生成数据。由于多线程并发顺序不定,很难自动测试,也很难定位bug,因此只能根据输出情况捋一遍运行流程,大体给bug定位。除了简单的bug,比如死锁,一些诡异的bug只能靠时间去堆。大胆假设哪里有bug,小心手工求证。我就在第三次作业发现了这样一个诡异的bug,具体请参见第二单元总结-bug部分。

第三单元:初步接触JUnit单元测试。单元测试只能测出本单元有无问题,理想的情况下,各个单元没有问题,合起来也就没有问题。实际上可能并非如此,因此,需要多个单元共同测试,或者JUnit白盒+随机数据黑盒共同测试。据航天科技来分享技术的嘉宾称,白+黑也是目前业界主流的测试手段。

第四单元:这个单元比较朴素,各个方法代表相对独立的指令,彼此没有关系,而且每个方法逻辑上并不复杂,因此,保证各个方法没有问题,合起来就一定不会出问题。因此,合理构造UML图,将运行结果与期望结果对比,最为简单粗暴且有效。我构造了足以覆盖各个指令的全部情况的UML图,为R001~R009分别构造了UML图,测出了不少bug。

三个具体的改进建议

  1. Pre部分到第一单元跨度太大。Pre过于简单,第一单元指导书是所有单元最长的,定义是最多的,算法是最难理解的,码量也不小,大家都很折磨。建议加大Pre难度,并把Pre计入课程成绩,以督促同学们在假期完成Pre,给第一次作业留出充足的时间。甚至可以提前作业发布几天,进行递归下降的引导。

  2. 突出博客作业的意义。我的博客作业至今没有被任何老师或助教评价,我完全得不到任何正面或负面反馈,写的一头雾水。据观察,一次博客作业中被点评的同学比较少。博客作业都是同学们用心写出来的,图也是用心画的,反映的都是自己的真实感悟,没有任何反馈就很没有写下去的动力。希望下届至少可以每个同学点评一次吧。

  3. 图论算法有点多了。虽然用到的每一种图论算法都有我在OJ上的提交记录,但客观的说,这届6系是自主招生取消的第一届6系,大部分同学对算法的理解辄止于ds。就连老师也说:

    “我希望你们若干年后,如果被问到OO课学到了什么,想到的不要是我在某次作业用了并查集啊、我用了xx算法啊……”

    不幸的是,这些东西给人的印象确实挺深刻的。OO本身难度已经不低了,如果一定要用一些课外的东西,来填充OO宇宙内容的空白,那么或许可以雨露均沾一些?时限卡一卡,整点高级数据结构?或者来点DP?

“满纸荒唐言,一把辛酸泪。都云作者痴,谁解其中味。”

且住。

posted @ 2022-06-28 23:21  Barque  阅读(9)  评论(1编辑  收藏  举报