BUAA_OO_2020_UNIT4_Summary

BUAA_OO_2020_UNIT4_Summary

一、第四单元作业总结

​ 本单元的主要任务为对uml图(包括类图、状态图、顺序图)进行解析,并对相应的查询指令给出正确的结果。重点在于理解uml图元素的组织结构,以及架构设计

HW13

​ 本次作业实现较为简单,重点是理解uml类图。由于基础uml元素类(UMLElement等)中仅包含了最基本的信息,如id, name, type, parent_id等,考虑到扩展性,因此对几乎所有实体元素类设计了包装类(除了最底层元素parameter和仅表示关系的元素generalization等)。包装类中的除了包含基础元素类外,还有各种容器管理下层元素和元素间的关系,以及协助完成查询指令的相关查询方法。

​ 由于MyUmlClass类和MyUmlInterface类具有较多的共性,如均可有方法、属性等元素,均有继承关系,均有关联关系等,因此对这两个类又抽象出父类MyUmlModule,包含共性的元素和方法,两个子类则仅添加独有的元素与复写方法

​ 本次作业一个较为巧妙的设计为,增加了一个MyUmlObject类,继承自MyUmlClass类,改写了必要的查询属性方法(需要统计所有父类相关内容的查询方法,如getAssociationCount,复写为返回0),且采用单例模式,避免实例对象太多浪费空间。将每个解析出的MyUmlClass对象初始父类设置为MyUmlObject类的唯一实例,使得每个MyUmlClass对象在查询时均有父类,从而避免了反复对是否有父类的判断,代码更为简洁,同时语义上更符合java。

​ 此外,基于工厂模式的思想,将解析器类MyUmlInteraction的初始化工作,抽象到单独的类InteractionCreator中完成,避免一个类的代码过于冗杂。

​ Uml类图如下:

HW14

​ 本次作业增加了对顺序图和状态图的解析和查询,类图查询与上次相同。架构设计与上次大致相同,对类图部分保持不变,为顺序图和状态图的部分元素类设计包装类,完成元素间的关系组织以及查询。

​ 为了避免总解析器类MyUmlGeneralInteraction过于臃肿,将具体的业务逻辑处理代码按照图的类别放到三个下层解析器类中MyUmlClassInteraction, MyUmlCollaborationInteraction, MyUmlStateInteraction,实现相关的接口,并设计各自的Creator类进行对象生成。总解析器类中包含三个下层解析器对象的属性,对于各种查询指令调用相应对象的相应方法即可。

​ 对于三个Creator类,将它们的共性抽象到父类InteractionCreator

​ 此外,设计Type类,包含一些static的属性和方法,完成有关元素类型的处理判断等

扫描二维码关注公众号,回复: 11319472 查看本文章

​ Uml类图如下:

HW15

​ 增加了对uml图(类图和状态图)的检查,其余与前一次作业相同。架构设计基本不变,增加了类MyUmlStandardPreCheck,实现接口UmlStandardPreCheck,完成检查任务的业务处理。总解析器类中增加该类对象的属性,对于各种检查方法直接调用该属性的相应方法即可。

MyUmlStandardPreCheck的构造方法需要MyUmlClassInteractionMyUmlStateInteraction类的对象,以及元素类信息HashMap<String, UmlElement> elements(均由总解析器类提供)。检查类从两个下层解析器类中获得MyUmlClass, MyUmlInterface, MyUmlStateMachine的List,用来完成相关检查方法,因此上述两个下层解析器类需要提供相应的getList方法。

​ R001和R005-008的检查均是,在已有元素包装类中添加方法,并调用这些方法来完成。R002-004的检查需要借助图类算法,因此设计节点类Node和算法类Taijan,将类和接口元素抽象为点,将类、接口间的继承、实现关系抽象为边,通过上述类和接口元素的List构造独立于uml元素的节点图map,完成相关的检查任务,具体算法如下:

  • R002:对循环继承的检查。对所有类和接口元素构造相应的Node对象,仅将继承关系作为边加入进去,使用Tarjan算法,统计出所有包含两个及以上点的强连通分量,和有自环的单点强连通分量,上述分量中的所有点对应的类/接口元素即为循环继承的元素
  • R003:对重复继承的检查。保持R002中构造的图不变,递归地统计每个点到所有可达点的路径数(只有一条记作1,若有多条则数目记作2),注意进行标记防止重复搜索。之后遍历所有节点,若某节点到其一个可达点的路径数为2(有多条),则该点对应的类/接口元素则为重复继承的元素
  • R004:对重复实现的检查。在R002图的基础上,将实现关系作为边加入进去,清除所有点的标记,采用和R003相同的方法进行路径数统计,最后针对所有类元素对应的节点进行相同的遍历统计。根据检查的顺序和java单继承的特性,若类元素节点到其某一可达点的路径数有多条,则必为重复继承引起,从而保证了正确性

​ 程序的uml图如下:

​ HW15的复杂度如下:

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

第一单元

​ 本单元的任务是处理不同类型函数的求导,重点是层次化设计方法以及继承、实现、多态的应用。

​ 关于架构设计。第一次作业比较简单,设计了几个简单的类,没有过多的考虑扩展性,总体上是面向过程的思维。第二次作业考虑到了扩展的需求,并将输入处理、求导、优化等部分分开实现,采用统一接口和多态设计。第三次作业由于前一次留出的可扩展性,仅在第二次作业的架构上扩展完善即完成。总体架构是以“表达式-项-因子”的层次为基础,同时以因子为核心,进行设计的。

​ 关于OO方法,有以下几点感受:

  • 层次化的设计:自顶向下的设计方法,将一个大的任务/模块分解为较小的任务/模块,逐层细化,最终将难以完成的难题分解到一个个能够实现的小步骤
  • 类粒度的划分:面向对象程序设计的一个关键步骤即为对类粒度的划分,使得每个类各司其职,只管理并管理好属于它的全部信息,实现高内聚低耦合的设计
  • 继承、实现、多态的应用:java中继承实现多态的机制,让上层类可以仅依赖抽象而不是具体,从而使我们可以在一个更为抽象的层次上进行设计,同时程序的可扩展性与好的维护性也依赖于这种机制
  • 设计模式:本单元中主要用到的设计模式是工厂模式,包括简单工厂模式和抽象工厂模式等。工厂模式可以将复杂且麻烦的生成对象的过程与具体业务逻辑处理代码分开,使得程序架构更为清晰,代码更加简洁
  • 正则表达式:好的正则表达式可以高效的处理文本、提取信息。此外,使用正则表达式也能够使代码更为简洁、可读性更强

第二单元

​ 本单元的任务是电梯并发调度问题,引入了多线程,重点是多线程程序设计以及线程安全问题的解决

​ 这一单元的架构大致相同,也较为简单,总的来说是将任务分解为“请求输入—调度—电梯运行”的模式,不同也仅在调度算法的改变会影响调度器和电梯的对应关系。该架构的优点是简单易于理解和实现,缺点是较为僵硬,没有办法很好地加入更复杂的调度算法(如模拟器等);扩展性不够强,对于更复杂的需求(比如讨论区天马行空的需求)大概率需要重构;电梯类较为臃肿,包含所有四个状态的业务处理。

​ 关于OO方法的感受如下:

  • 并发控制:使用范围更细更精准的同步块,将线程类与公共资源类严格分开,能够有效避免线程安全问题,同时提高并发效率
  • 生产者-消费者模型的运用:事实上本单元的设计核心便是生产者-消费者模型及其变体,这是最基础也是最为有效的并发模型,充分运用并理解该模型能够优化设计、提高效率
  • 类的状态的理解:这是本单元做的不太好的一点。在之前的代码设计中,重点关注的都是类中包含的信息及其处理,对于类本身的状态并没有太多的思考,导致了本单元作业中,由于电梯状态较多且业务逻辑均不同,从而电梯类较为臃肿。仔细思考和参考谈论区后,认为可以通过内部类和更加精细的层次划分解决。

第三单元

​ 本单元的任务是社交网络的设计,引入jml,重点是理解并实现jml规格(事实上是简单算法设计和性能优化)

​ 关于架构,本单元的架构较为单一,与jml规定的过于详细有关,对于业务逻辑处理相关的架构比较固定,在此不谈。比较灵活的是算法设计部分,将算法独立成类,减小核心数据类的复杂度,同时也便于扩展和替换不同的算法。

​ 关于OO方法:

  • jml规格的理解:这是本单元的核心,理解有误满盘皆输。在实现代码时既需要完全理解规格本来的意思,防止出现错误或疏漏;又不能拘泥于规格的本意,要在充分理解的基础上,选择更高效的等效算法来实现,否则效率较低。
  • 图数据结构和算法:本单元的真·核心。图有关的数据结构和算法可以解决许多问题,是我们无论何时都要熟练掌握的技能
  • 性能优化:除了选择更高效的算法外,还有设计层面上的优化,比如缓存机制等。同时在进行性能优化时,也训练了复杂度估计、时间估算等能力

第四单元

​ 本单元的任务是解析uml图并处理相关询问,重点是理解uml图,与架构设计

​ 关于架构,本单元三次作业的具体架构在第一部分已详述

​ 关于OO方法:

  • uml图的理解:对图中元素的理解和元素间组织关系的理解,事实上本单元若理解了uml图,则代码实现较为简单(当然仍要注意效率和细节)
  • 层次化设计plus:uml图元素本就是一个层次化的架构,在此基础上,针对不同的业务逻辑,将任务/模块细分,最终形成一个好的、扩展性强的架构
  • lambda与Sream的学习:本次作业学习并简单应用了lambda表达式与流机制,结合容器的foreach方法,使得代码更加简洁高效

三、四个单元中测试理解与实践的演进

​ oo作业除了编写程序外,每次的测试也是投入与收获较大的一部分

​ 最大的收获可能是学习了python语言,并通过它构造自动化评测机(所以说oo绝世好课,一门课学两门语言)在此之前则对评测机的搭建完全没有概念,oo课程第一次让我意识到了代码在其他方面的作用

​ 总体上每个单元是在第一次作业时搭建好整个评测机的架构,由于每单元的均是迭代开发,测试框架基本不变,因此后两次作业只需要将评测机稍加修改即可应用与测试。除了自我测试外,评测机还可用于互测阶段,通过黑箱测试发现bug数据,从而找出bug。测试的过程均可分为:生成数据 -> 运行java程序得到输出 -> 检查输出结果三个阶段,每个单元的具体测试情况如下:

  • 第一单元,表达式求导。数据生成使用python的xeger库,通过正则表达式反过来得到符合的随机字符串,实现数据随机性和鲁棒性。运行程序则借助python的os、subprocess库通过系统调用接口实现。检查结果则是使用sympy库,得到正确的求导结果,再按照指导书中的检查方式带值计算,进行结果比对来判定正确性。整个过程完全自动化,效果十分显著。

  • 第二单元,电梯运行。数据生成较为简单,不过多叙述。运行程序方面,由于需要在一定的时间投放数据,因此使用subprocess中的Popen开一个新的进程p,同时借助time的sleep函数,每条数据在sleep相应时间后,再投放到p进程的输入中。检查结果则是另写了一个java程序来完成,使用异常机制来简化检查的代码,并给出电梯系统运行成本(运行时间 + 等待时间)

    第二单元的评测机不仅用来测试,还可以用于对比不同调度算法的性能,从而选择在随机性较强的数据下,性能较好的一个方案

  • 第三单元,社交网络。通过编写java程序来完成数据生成的任务,并通过命令行参数选择不同的数据生成模式,来得到不同类型的数据(比如针对某种指令的爆破数据),从而充分测试。运行程序与之前相同,检查结果则只能采用对拍的方法来尽可能发现问题,你需要有一个可靠的伙伴。此外评测机还要对运行时间进行检查。

    除了自动化测试,第三单元更多的是形式化验证、极端数据测试以及阅读代码。由于本单元强调jml规格,因此基于逻辑的形式化验证是必须且有效的,同时由于重点在于算法,则可通过手动构造极端数据测试正确性和结果。在互测阶段,因为架构彼此差不多,比较容易读懂代码,所以大部分bug均是阅读代码后发现

  • 第四单元,uml图解析。由于数据测试较为困难,加上邻近学期末,因此只搭建了运行程序 + 对比检查结果的对拍脚本,手画uml图(多是极端情况),来进行测试。更多的检查是反复阅读代码和形式化验证。

​ 总的来说,经过四个单元的学习,我认识到测试是编写代码中十分重要的一环,充分地测试才能有好的代码质量,测试是发现代码问题最直接效果最显著的方法。

​ 但是,测试也有着局限性,比如第二单元的多线程程序,许多线程安全问题引起的bug在本地测试中根本无法复现。因此我们需要测试,但又不能过度依赖测试。测试只能尽可能的帮助我们发现问题、提高代码质量,是一种辅助性的工作,更重要的,还是设计一个好的架构,不断地形式化检查,来减少错误的发生。

​ 对于测试部分的感想,用助教的一句名台词来作为收尾:“一万次测试的通过也无法保证程序百分百正确,但一次测试的失败则足以证明代码一定有问题。”

四、课程收获

​ oo课程真可谓是宝藏课程,助教和老师们精心设计的作业,中测、强测、互测、bug修复各个环节的训练,包括每单元的总结,都让人收获颇丰

  • 首先是最基础的语言学习,即java和python这两门语言
  • 然后是面向对象设计思维,包括层次化设计、继承多态的运用、各种设计模式的学习等,使我在面临各种问题时,能够更多地从对象的角度而不是过程的角度来思考,将问题逐层分解,最终一点一点的解决
  • 对于软件工程的理解:之前写代码的模式都是想到哪写到哪,通过一学期oo课程的学习,让我认识到了架构和设计的重要性,能够从更高的层次上来解决问题
  • 自学能力的培养:为了更高效地管理数据去网上自学容器,为了深入理解容器探索jdk源码,为了更好地架构去学习设计模式,为了自动测试去学习python和评测机搭建,等等。oo课程设计许多方面,也遇到了很多问题,让我有了独立思考问题,通过各种手段解决问题,从而学到知识的较强的自学能力。

​ 此外,还有研讨课上与同学交流时的思维碰撞,每个单元相关的主题知识(多线程、jml、uml等),都让我学到了许多。

五、课程改进建议

  • 实验课能否在一段时间后开放并提供答案,让我们能够对学习成果和实验完成情况有所了解,更好地帮助我们进行之后的学习。而不是每次实验课后都云里雾里,压根不知道结果是否正确,心里没底的同时,训练效果恐怕也不尽如人意
  • 第三单元总结时,对jml工具链的使用环节能否取消。主要是官方也没有较好的工具链,使用时也是错误百出、十分痛苦,破坏了课程体验也浪费了时间
  • 个人感觉第四单元的指导书不如前三个单元严谨,尤其是最后一次作业(可能助教忙于烤漆),指导书中有一些有歧义的地方需要借助讨论区解决。当然我明白助教们的繁忙,只是建议可否能够通过任务规划,比如更早地完成第四单元的指导书或者交给更多的人来完成,能够在不增加助教负担的情况下,有一个要求更明确的指导书(可能助教也是在提前训练我们理解客户要求的能力)
  • 能否适当增加有关多线程的作业训练量和种类,感觉仅仅通过一个电梯单元的训练仅达到入门水平,可否提供更多形式更丰富的作业训练
  • 鼓励同学们分享自己的优秀设计,仅仅一个点也可,而不是整篇贴上优秀代码,若没有辅助说明(注释,架构介绍等),会打击阅读代码的积极性,增加阅读代码的难度。而且仅通过阅读代码不一定能get到优秀设计的点,希望之后这方面能有所完善。

六、线上学习OO的体会

​ 作业方面影响较小,影响较大的是交流方面。线上打字交流的效果总是不如当面谈,理论课也缺少互动,研讨课也无法及时得到同学们的反馈,最重要的是不能欣赏助教和老师们的绝世容颜

结语

​ 在课程总结的最后,要感谢一直在背后默默付出的全体助教和老师,正是助教和老师的辛勤工作,使得oo课程的体验越来越好。希望我能够在来年加入oo助教组,让学弟学妹们享受到更好的课程内容。

猜你喜欢

转载自www.cnblogs.com/wangyikun/p/13166502.html