OO的奇妙冒险4

OO的奇妙冒险-4

总结本单元作业架构

第一次
  • 从UML作业开始,代码的复杂度和对架构的要求真正出现了。不同于以往的作业,在UML中一个好一点的架构带来的是更少的bug,更快的debug和更少的代码量。总的来说,我认为只有UML这两次作业,多项式第三次作业和电梯第三次作业这4次作业才有谈论架构的意义,因为思考架构带来的收益和能力提升

  • 我在做这一次作业的过程中,经过不断地摸索和与同学讨论,经历了4次重构,最后一次才确定了一个好写舒服的架构。这里实名感谢wsb大佬,与他的讨论激发了我很多新的想法。事实证明,好的架构是有很大帮助的,我最后一次从0开始写完整个作业仅用时不到2h

  • 首先,仿照web框架,将前端后端和数据库这三者分离解耦。数据库负责快捷查询,配合造好的ID可以快速建立,也可以查询或新增自建的类或模型。前端负责交互界面以及简单的逻辑引导,后端负责建模和计算。计算方面,全部采用无视性能的写法,仅有的非朴素的处理是记忆化。经测试,这样写出来的代码短,快,性能够活。

第二次
  • 鉴于上一次的作业架构自我感觉良好,我这一次作业直接继承了上次的左右代码,仅在有关associationEnd的处理上方法级重构。
  • 考虑到三个图分别独立,没有查询上的依赖关系,因此我选择分别建模,分别构造顶级类,并分别存入数据库中。在前端构造器进行分类与引导即可。有关检查,分别采用Set判重,floyd查距离和hashMap数次数的朴素方法,时间效率没有明显问题
  • 本次作业的难点是状态图的建模,但经过化简就变得十分容易了。重点还是在于好的架构能够带来的开发效益。这次作业也是我唯一一次代码量超过1000。事实上,如果根据简化的状态图建模,应该能压回三位数
总结
  • UML单元的作业我认为是真正的OO作业,几乎完全抛弃了字符串处理,性能优化,算法选择这些看起来和工程关系不是特别紧密的东西,完全由架构和设计思维说了算。在UML单元一个低耦合的架构对开发测试扩展的提升是显著地,与之对应的是电梯或者多项式单元,一个工程性看得过去的架构可能给优化带来负担。
  • 但是,纯架构设计带来的一个重要问题,就是边界条件和概念定义的繁杂。可以看出,相比之前作业的指导书,尤其是多项式单元的指导书,本单元指导书的作用大大降低,很多地方完全没用,几乎都要依赖讨论疑惑暴动答疑帖这样的形式,可以说在数据范围和具体规范上,做的并不好,略显仓促。老师和助教的本意应该是让我们自行摸索,然而,面对实打实的分数,没有人敢轻易下结论。这就好比一个国王让农夫自行决定税收的份额,但决定权其实全在国王的手里。我认为,带有学分和分数的课程作业并不适合探索或自学任务。

总结架构设计和OO方法理解的演进

前两单元
  • 这两单元放在一起说,是因为有一个神奇的制度——性能分。不是静态的性能分,而是动态的,要求同学们相互厮杀的性能分。这样的制度带来的唯一结果就是要么全体摸鱼(激励太少),要么全体爆肝囚徒困境(激励太多)。
  • 因此在这两单元,架构设计并不是重点,甚至不是值得考虑的点。因为实现难度不高(或过高),唯一的重点便是性能优化。面对互相损害的制度,好的优化必然要抛弃可扩展的架构。虽然课程组一再强调:可扩展和增量开发是课程目标,但至少就我个人,或者身边的几位同学看来,面对仅三四百行的程序和可观的性能分,一次作业重构一次,交完就扔才是好的选择。可以说,课程制度本身并不符合课程目标的期望。
  • 结合以上原因,我在前两单元并没有任何架构设计。虽然我意识到这很重要,但我仍主观客观上都不希望去考虑架构
第三单元
  • 这一单元的架构设计有了一定的意义,然而,又遇到了另外一个问题:作业难度太低了
  • 这一单元的作业基本能够在半天的时间内完成,算法部分伸手讨论区即可。面对半天工作量+算法主导+三四百行的工程量,谈架构就是牛刀小用,毫无意义。我设计了几个架构并做了一定的尝试,但是可行性和实际意义都不大,不如直接一个400行左右的类直接干净利落解决战斗
第四单元
  • 这一单元排除掉数据范围不清楚,定义不清晰等问题,是我个人认为最符合OO思维的一次作业。主要就在于,设计和面向对象的思维终于取代了其他的事物成为了最重要的目标。虽然我个人也喷过这一次作业的种种问题,但和作业的大方向是没有关系的。
  • 我在这一单元的作业中,依次浅薄的自学了设计模式,架构设计,以及初步的前后端分离。我利用设计模式达到了一定的解耦效果,配合前后端分离的总体思路和先设计再动手的整体思想,终于达到了一个令自己满意的方案。具体的类图和复杂度统计我就不贴图了,除了交互界面的构造器和check008之外,没有爆表的项。这两项也可以通过分级处理,将任务转移到下层或后端来解决。

测试的理解与实践的演进

对拍器
  • 面对OO这种快速开发+小工程+统一正确性/性能测试的模式,对拍器是最好的测试手段,没有之一。
  • 对拍器,用更专业的说法可能应该叫批量化黑盒测试,可以自动的利用随机性进行覆盖+暴力测试。由于是黑盒测试,他可以模拟真正的用户输入并检测输出。面对多线程程序,尤其是长时间交互的多线程程序,对拍器可能并不好用,但在OO作业的范围中,有较好的解决方案
  • 第一单元我没有任何高效的测试手段,只靠纸笔和大脑进行论证,顺便白嫖了同学的对拍器。为了完成互测任务,我在工程内部建立了对拍器,缺点是针对一个互测屋的同学,从下载源码到可以对拍大概需要20分钟的准备,很low
  • 第二单元在wsb大佬的思路下,我采用了10倍速的测试模式,即将所有任务的完成所需时间缩短为0.1倍,加快对拍。这也是前文提到的长时间交互程序的处理方法。这一单元数据生成器十分好造,重点不在于IO的准确,而是调度的性能。因此对拍器更大意义上的作用是统一某种生成策略对应的平均时间
  • 第三单元我抛弃了java,转用shell搭建对拍器。这样的好处是轻量高效,只需要可执行文件而非源码即可进行测试。而测试的类型也由性能测试转向了正确性测试,因此制造数据生成器反而成为了重点问题。在lsj同学的帮助下,我明白了自己数据生成器的弱点,也掌握了边界数据的一些制造方法和参数化方法,完善了对于生成策略参数化的理解。经实地检验,面对我和我舍友2个互测屋的十几位同学一起测试,lsj同学的生成器出bug率达到了100%,而我的生成器只有约10%
  • 第四单元作业仍然是生成器制造导向的测试。我一开始的生成器思路是,手动构造一个复杂的不好看的图,然后遍历所有可能的查询,并增加非法查询和重名查询,即模型复杂度一定的情况下,遍历上万种可能进行覆盖性查询。wsb同学的生成器是更加高效优异的,他可以直接跳过建模步骤,自动生成json文件进行测试。经过反复的打磨与讨论,他的生成器能够生成几乎合法的数据,十分强大。在这里实名感谢wsb大佬最后一次作业测试出了我的一个有关次数统计的bug
单元测试结合JML测试
  • 在上一单元的博客中,我谈到了自己对于这一对儿“难兄难弟”的看法:单元测试解决覆盖率JML解决边界条件。在作业中得不到应用,主要原因是作业的工程规模过小,并且缺乏交互性。试想,针对win7操作系统这个软件,可能使用对拍器这样的手段进行测试么?单元测试重在单元。可以说,单元测试生不逢时,面对小工程没有太大作用。而JML完全没有社区支持,几乎没有工业化,商业化,所以几乎没有作用。
总结
  • 课程组推崇的Junit和JML完全没有应有的作用,狭义的来说并没与达到课程目标。我认为这里的原因与上文一致,是课程本身的作业阻碍了Junit的推广。平均1-3天写完的工程,可以说是比敏捷开发还敏捷,基本就是高级java程序设计。OO和工程化的思想有帮助,但并不能完全体现。连架构,OO本身都难以幸免,何况Junit呢?我相信大部分同学使用的测试手段都是对拍器,我自己更是除了课程实验外没有任何一次使用对拍器以外的自动测试手段。
  • 但反过来讲,Junit在快速定位bug的时候还是有用的,但这仅限于代码有良好的注释与架构,并且时间充裕,因此互测中完全没有必要采用Junit。很多时候代码不是自己写的连看都看不懂。总的来说,小工程小输入低交互带来了强大的对拍器,在这种特定环境中完全取代了Junit的作用。

课程收获

  • OO课程最大的收获应属开发测试管理200-1000行左右软件的工程能力。数据结构课程完全面向过程,管理400行左右的大作业已经略显困难。但在OO思想和OO语言的帮助下,管理1000行左右也不是大问题。课程没有选择C++而是java,可能入手起来不是特比顺畅,但反过头来看,彻底抛弃面向过程和内存相关的指针,未必是一件坏事。java更慢的运行速度确保了使用官方库是最好的选择,而不是像C++一样为了性能需要手造容器。

  • 除了开发本身,对测试的提升和个人收获也不少。结合在OS课程中学到的一些实用技术,进行本地的小规模测试对我来说已经从黑科技变成了很普通的手段。甚至在面对bug的时候,我第一时间都会选择再拍几组找共性而不是直接读相关代码。

  • 增加了对代码风格以及可维护性的重视程度和思维理解。第一次作业时有大佬在讨论区建议增加warning检查,我当时比较反对,认为这样会增加更多的限制,写起来很难受。但其实经过一学期的学习,增加一些限制带来的是更好的可维护性,后者的重要性更大。面对DS课程写出的代码,我已经几乎无法阅读,但我相信编译学完后,我面对自己的OO代码仍能回忆出自己现在的架构设计总体思路等等。

  • 其他的收获:

    这一部分比较杂,大概有

    • 信息搜索能力
    • 上手新语言和IDE的能力(实名赞美IDEA)
    • 团队作战能力(没有同学互帮互组讨论交流对拍,我活不到OO结束。实名感谢wsb,wzm,lsj,xcb等大佬对我的帮助)
    • 认识到工业界真正的开发和需求一些相关的知识(实名感谢zsa,zyf,nyz等助教大大)

课程建议

这一部分我大概有几个方面的建议,具体分为:第一二单元第四单元互测实验课

  • 第一二单元难度略高,且总体来说不像是OO课程作业而是博弈论和java程序设计课程。原因我个人总结出在过小的工程量和略不合理的性能分制度设置上。经历了DS大作业的刷分,我对于排名竞速这种东西可以说是比较反感,尤其是作为一个代码能力并不太强的人来说,一方面是自己的主观原因,另一方面是确实很高的客观难度。作为零基础选手,想要在DS大作业中拿到前几名的名次,我付出了非常多的精力,整个6月份连通烤漆后的几天我几乎是全部投入了进去,还经常熬夜。OO作业并没有那么充裕,也因此显得更窘迫。

    我的建议是,更改排名模式为静态模式。比如电梯单元,给出数据生成器策略,提前规定静态得分。具体形如:

    • 40秒内每秒1个请求,楼层在-3到20之间完全随机分布,规定55秒完成为满分,95秒为80分,线性分布,高于95秒按80计
    • 0秒瞬间投放40请求,楼层完全随机分布
    • 50秒内完全随机40个时间,楼层到达1或15的概率是其他的3倍
    • 前20秒从16-20出发的概率为其他出发楼层的3倍,后20秒从-2-1出发的概率为其他的3倍,40个请求各自的时间完全随机
    • 40秒每秒1个请求,到达楼层完全随机,出发楼层为3楼的概率为20%,剩余楼层平分其他概率
    • 以上5种策略每种生成10组测试样例,共计50组,每组策略附带有类似第一种的得分规则

    诸如此类的具体策略,一方面可以更好地确定真实的情况,另一方面方便同学进行自主测试,同时方式同学们互搏带来的消极心理体验。的确,排名分数更有利于激励竞争,但是比较违背人性,我个人在看到这样的制度时是很反感的。另一方面,多线程本身开发难度也不低,因此建议取消没有意义的第五次作业,直接进行没有性能分但有Tmax的Look调度策略作业(个人认为Look比ALS有实用价值),以及有完整性能评价的大作业,时间两周,供同学们充分思考,优化。

    甚至,可以调换多线程大作业和JML两单元的顺序,毕竟JML作业是有助于架构设计的,而电梯调度更偏向于综合应用。

    同时,第一单元可以仿照第二单元进行静态评价的更改,也可以完全替换为全新的,更具有OO入门意义的作业,比如某次实验课中出现的员工管理系统。个人感觉员工管理系统做下来对OO的认识比字符串处理和三角函数优化的第一单元舒服一些。

    但是,从我个人内心来说,不太喜欢性能分的机制,因为性能分本身与架构设计和工程性没有关系,甚至反其道而行之(越高耦合的架构优化效果越好)。但考虑到性能评价也是工程能力的一部分,建议大幅修改性能评价机制,将其拔高到与正确性评价相同的地位,并且做到公平,独立。现在这个简单的排名性能评价,既使人心理不舒服,又不一定能提升多少能力(比如,电梯调度算法的细节部分可能一辈子就用这一次了)。课程希望的:好的架构优化一定好,坏的架构一定不好做优化在很大程度上是不太准确的。我个人认为我第二次电梯的架构就是一坨屎,但是却取得了比第三次高的多的分数,可见现有的制度不能防止差架构+高耦合这样的奇葩通过强测。

  • 第四单元

    第四单元的意见前文已经提过,不在赘述。我个人愚见,不可能在课程作业这样的形式中鼓励同学自我探索。一份不清晰不严格的指导书,或称之为需求,带来的只能是烦躁,疑惑甚至暴动,谩骂。虽然以后实际的客户会比任何作业指导书提出的需求都奇葩,都恶心,但在作业中实行这样的制度我仍然认为不妥。我的建议是仿照前几单元的指导书,继续严格数据范围与定义。事实上就算在有了严格的定义的情况下,好架构坏架构的区别仍然明显,UML作业中一个差的架构的唯一结果就是屎山。

  • 互测

    我认为互测有参与意义的,唯有第一单元。面对WRONGFORMAT的攻击,大家参与都普遍很高

    但到了第二单元及之后,互测几乎变成了摆设。我所知道的是,像wzm这样测试能力很高,测试意愿也很高的大佬,在二三单元的作业中没有任何一次成功hack,如此低的hack率是能说明一定问题的。互测现在处于一种很鸡肋的位置。如果第一单元不改的话,那我支持第一单元全程攻击WRONGFORMAT,毕竟鲁棒的输入输出处理和异常检查是很重要的。第二单元建议彻底分开,改变互测机制。中测加强,让正确性bug更少,强测采用更良好的,更完备的性能评价体系。同时由于公开了强测数据策略,针对性的优化势不可避,互测则作为全面性的弥补,建议每个人可以构造符合基本规则的任意数据,如果超出了Tmax的时间,则视为一次hack。在此基础上,严格限制hack次数,比如每个人一次作业最多被hack两次。

  • 实验课

    直说吧,我个人认为实验课在今年的制度下没啥意义。唯一的意义可能是强迫及时自学,为作业提供一点点的帮助。究其原因,在于上午讲下午练的神奇时间安排,和画风清奇的实验题

    有些同学喷前几次实验过难,但我恰恰认为前两次实验才是有意义的实验,如果能够时间错开,那真正的起到了实验的效果,尤其是员工系统那一次,是我认为出的最好的一次实验。

    后面的实验总体来说逐渐流于形式化,与作业脱节。比如JML,Junit以及UML的检查。JML感觉略废,Junit限于工程规模,UML检查太广,又恰逢考期,完全没有经历去搞。可以说这几次实验对作业是没有实际帮助的。

    回过头来想,我认为Mooc中一次实验几个小任务课后增量的模式是比较好的,比如员工管理系统,完全可以扩充成一次对于面向对象基本特性(继承,多态,封装)的良好理解。配合没有什么优化和性能的第一次字符串检测,可以说基本扫清了java入门的障碍。我认为在此之上再配合一个JML第2次作业有关容器和库的选择与使用,就差不多了。

    实验课因其过短的时间,不可能面面俱到,今年面面俱到的结果就是啥都没捞着还落得个鸡肋的名声。我认为,完全可以利用实验课实践,体验java一些语法,比如泛型,比如函数式。这些语法在课程组看来可能是没用的,但其实对于很多具体的开发来说非常有用,塞到实验课中可以说是比较贴切。我个人是从员工系统那一次开始正式使用和理解继承多态封装这几个概念的。

总结

  • 不管怎么说,OO还是完结撒花了。虽然也有这样那样的小问题,但总体收获还是比较大的。

  • 希望在身边大佬的继续努力下,给1806提供一个更好的OO课程!

原文地址:https://www.cnblogs.com/shhh2000/p/11076678.html