OO第四单元暨课程总结

一、第四单元架构设计

1. 第一次作业

第一次作业在设计时可以说没有考虑扩展性,直接建立一个类——MyUmlInteraction,将所有的代码全部堆砌在这个类中。使用大量的HashMap,以UMLElement的Id为key,保存该Element对应的内容,如使用HashMap<String, ArrayList<UmlAttribute>>存储类或接口的Id到所有参数的索引映射。

2. 第二次作业

第二次作业受到讨论课中同学们的启发,同时考虑到类图、顺序图和状态图的独立性,分别使用了三个类管理三种图的元素,通过一个MyUmlGeneral类保存三个类的实例并调用这三个实例的方法完成功能。

但每个Manager内部依然是通过大量HashMap来存储索引信息的,并没有对各种UmlElement做数据抽象,因此代码比较长。

3. 第三次作业

第三次作业引入了UML检查,直接在ClassManager中添加新方法已经过不了Checkstyle了,因为每个方法的代码实在太多,而针对类的UML检查又十分复杂,因此引入了ClassInfoExpert和ClassGeneralizationGraph类,前者负责管理ClassManager之前管理的所有信息(主要是各种HashMap),后者负责将类和接口之间的泛化和实现关系转化为有向图并在此基础上检查循环继承、多重继承、多重实现等问题。

对R002、003、004的检查最终分别会调用ClassGeneralizationGraph中的checkRings、calcAndCheckGeneralizationCount和calcAndCheckRealizationCount。在ClassGeneralizationGraph中会直接抛出R002、003、004异常,ClassInfoExpert、MyUmlClassManager和MyUmlStandardPreCheck不会对其进行catch,而是一直throw到MyUmlGeneral中,最终由MyUmlGeneral抛出到外界。其他的检查也是类似。

总的来说,在本单元作业完成的过程中由于时间比较仓促,在设计上做的文章就比较少,导致自己的架构并不十分清晰,而且缺乏可扩展性,在设计水平上个人认为与前三个单元相比反而有所退步。

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

1. 架构设计的演进

回顾整个课程,第一单元和第二单元毫无疑问是最考验架构设计能力的两个单元。第一单元需要考虑的细节和数据种类很多,需要健壮的设计来支撑程序功能的正确完成。而第二单元涉及到多线程问题,架构不仅决定了程序效率,甚至直接影响程序的正确性,可能会由于线程安全和死锁问题导致程序运行出现严重问题。

但在进行第一单元作业时个人对OO的理解还很粗浅,因此设计中充满了各种假的OO,写出了很多耦合度很高的代码,扩展性也随之变得很差。而第二单元的过程中由于过分关注程序的正确性,导致类之间的关联虽然简单,但类内部的逻辑变得十分复杂,而且调度算法直接硬编码到代码中,扩展性也比较差。

第三单元的作业中由于要使用到许多的算法和数据结构,因此利用泛型封装了一些完整的算法和数据结构类,而程序的主体——Network、Group、Person本身并没有再进一步分解,每个类都直接管理了所有需要的数据,并严格按照JML的规格保证程序的正确性。

第四单元的设计由于时间仓促,做的功课比较少。但做完之后回头来看,与第一单元相比逻辑却清晰了不少,这也说明自己的架构设计能力在潜移默化中得到了提升。

2. OO方法理解的演进

如果说前两个单元对架构设计有着非常高的考验,进而迫使我们合理运用OO方法,那么后两个单元就是通过引入OO工具,主动引导我们深化理解OO方法中的各种概念和思想。

不得不说,OO这种将解决方案与问题域中的个体一一对应的思路确确实实简化了我们处理问题的过程。试想使用面向过程的思想完成第二单元的电梯作业,单纯将问题的解决转化为一个个过程本身就是十分困难且易错的。在前两个单元的作业中,我们尝到了OO的甜头,在实践中将OO的方法内化到我们的设计中;在第三单元,我们通过学习具体的OO工具和规格化方法,进一步认识对象和数据抽象的规范;在第四单元,我们通过学习强大的UML,将抽象描述的能力进一步提升。

从第一单元到第四单元,我对OO的理解从一个个单纯的将数据和操作封装起来的类逐渐演变为通过对对象的各种行为进行高度抽象以方便进行更高层次的设计和保持程序可扩展性和可验证性的强大的设计方法。

三、测试理解与实践的演进

虽然我们在第三单元学习了JUnit,但我至今对JUnit的理解还停留在手搓数据进行测试阶段。虽然单元测试这一方法是极有效的测试各个层次模块单元正确性的方法,但单元测试本身的测试用例本身还是测试用例,还是需要我们人为地设计或通过一些工具进行构造的。如果抛开那些尚不成熟的工具链,个人认为对每一个单元都设计一个测试用例构造器的工作量是有点太庞大了。因此在整个OO课程的作业中,我还是选择了针对整个程序进行黑盒测试的方法为主,这样我可以根据程序的预期功能直接设计输入生成器和输出验证程序,工作量会小很多,而且在大规模的覆盖测试和压力测试下,许多BUG也能够被发现,效果还是很显著的。

从第一单元开始一直到第三单元,我在实践中一直使用黑盒测试,在第四单元,由于数据相对复杂而时间有限,我选择了手搓数据进行测试,因此测试做的不是很充分。

单元测试和回归测试确实是现在软件开发中的重要测试手段,但这是建立在已经拥有强大并不断完善的测试集的基础之上的。对于从0开始的测试,我个人认为还是黑盒测试更加简单暴力一些。

四、课程收获

OO课不愧是专业核心课程,在学习过程有很多收获。最大的收获有三点。

第一,认识到了架构设计的重要性。之前课程中遇到程序题目都是拿起来就写,边想边写,导致最后写出来的程序结构糟糕,遇到问题只能东补西补,不仅效率低还容易出错。对于OO课程中的作业,这样做已经完全行不通了。没有一个预先设计好的架构,在写代码的时候没有一个约束对自己进行修正,很容易就会写偏写错,等反应过来的时候已经写了一大半了,但却因为不能完成功能而不得不重写。即使这次完成了,面对迭代开发大概率又要重写,费时费力。而有一个良好设计的架构,在编码的时候就能降低思考难度,专注功能实现,在后续迭代开发中也能很好地适应。最重要的:程序结构清晰,在做review的时候能看懂自己写了什么,否则写过的代码再也不想看,会影响学习的效果。

第二,学会使用抽象,从不同层次分别解决问题。随着程序规模的扩大,往往很难同时考虑程序中每一个部分的具体实现,这就需要我们使用抽象,在考虑程序整体结构和类之间关系时先将具体实现隐藏,减轻思维负担。在我们考虑好各个抽象之间的协作和关系后,再针对各个抽象进行具体实现。这一点和架构设计有共通之处,架构本身也可以是一层较高层次的抽象。

第三,测试。程序功能的实现只是开发的一部分,测试同样是关键步骤,并且很多时候需要花费的时间比写代码的时间还要长。OO课的分层测试和互测机制让我们充分认识到测试的重要性。虽然从因果关系上来讲,程序出错一定是因为程序的逻辑本身,但没有人能够总写出完全正确的代码,甚至很多时候对需求的理解也会出现偏差,如果没有各种形式的测试,那一定是将程序的正确性赌在了运气上,这是不严谨、不可取的。无论是通过手工构造还是按照一定规律生成,我们都需要足够量的测试集来对我们的程序进行充分的测试,包括对每个模块进行单元测试和对程序整体进行黑盒测试,才能最大限度地降低程序出错的几率。

五、三个改进建议

  1. 完成程序的时间有点短,尤其是在第一单元第三次作业和第二单元的后两次作业中,感觉时间有些不太够用。个人希望可以将指导书的下发时间提前一段时间,比如提前到周一晚,虽然这会导致作业发布早于上课时间,但是能给同学们更加灵活的时间安排。
  2. 实验课的考核标准不是十分清晰。有些实验需要我们完成代码,有些实验需要我们提交文字答案,有些实验二者都有,但并没有明确地说明如何对答案进行验证。希望课程组可以写明实验如何验证答案,比如对于提交程序的实验需要通过测试集;对于填空题是通过匹配模式、还是将答案填入程序中进行自动测试、抑或是人工批阅,等等。这样可以避免在实验过程中同学们纠结空格要不要打,大小写要不要管,名字不一致时怎么写等等细节问题。
  3. 希望减少第三单元JML的内容,比如缩减为三周,将多出来的一周分配给其他单元。JML相关的工具链太过古老,使用颇为不便,同时JML这种形式化规格描述虽然强大,但对使用者要求比较高,有自身的局限性,个人认为其他内容值得比JML花更多的时间来学习,更何况JML单元的作业最后本身也变成了对各种设计、权衡、优化的考察,这一点可以通过其他单元来进行,未必要和JML联系在一起。

六、线上学习体会

线上学习的过程是十分愉快的,因为在家里的压力比学校小了很多,而由于OO课程的特殊性,学习效果并没有差多少,每次作业的完成、研讨课的讨论、实验课的进行对于我都非常顺利,我想和在学校相比也没有太大差别吧。恰恰相反,录播课可以重复播放这一点让我们有机会重复观看自己掌握得不好的部分;文字形式的提问和交流也加深了我们对知识的理解,毕竟在写文字的过程中我们会对自己写下的内容进行反复斟酌和修改,在这个过程中对我们深化对知识的掌握是很有帮助的。

真的十分感谢老师和助教们在教学过程中的付出和辛劳,OO课程也一定会随着一次次教学实践变得更加内容丰富、结构合理、节奏紧凑、参与性强,为同学们带来更多不一样的收获。

posted @ 2020-06-18 11:38  fty1777  阅读(162)  评论(0编辑  收藏  举报