其他分享
首页 > 其他分享> > OO Unit4 单元总结&课程总结

OO Unit4 单元总结&课程总结

作者:互联网

Unit4单元总结与课程总结

​ 随着第四单元最后一次作业的结束,这学期的OO也就此落幕。在这一学期的“魔鬼”训练中,我也确实学到了许多技术知识与开发思想。总结是为了更好地提升,这里就对本单元与这一学期的学习做一个小结。

第四单元架构设计

第一次作业

​ 本次作业的核心是类和接口,而其他的UmlElementUmlAttribute都是隶属于类和接口的。于是设计的时候就想着使用自定义的类和接口对其他数据进行整合,设计了MyUmlElement类和MyUmlInterface类形成封装。由于学了UML类图,这里给出一个简单的示意图。该图中只展示了部分方法,以及部分相关。MyUmlInteraction掌握了所有MyUmlElementMyUmlInterface的信息,查询的时候只需调用他们的方法即可,而这两个类内部拥有足够多的信息来完成这些查询。

1

​ 既然这些数据已经被这个类掌握,于是我对这两个类的期望增加了,希望他们能够直接处理外部传入的请求。这样做有好处也有坏处,首先,MyUmlInteraction已经具备了比较多的功能,需要读取输入的数据进行分类构建,还需要对输入进行检查并响应,具有较多的功能职责。让自己设计的这两个类对其职责进行分担,使得其只需调用该类提供的方法便可以直接得到被查询者的相应属性。但缺点是对于类的职责定义就不那么分明了,其实从内部结构上讲设计的MyUmlElementMyUmlInterface确实形成了信息专家,给外部提供了需要查询的信息,不过这个操作在类的内部完成,某种程度上增加了类的逻辑复杂度。并且使得该类不只担负了储存信息的职能,还需要构建缓存,执行操作。不利于类功能的专一化。

​ 这里给出写本文的时候的一种较为合适的设计,可以考虑将构造器单独分为一类,将构造好的自定义对象传给Interaction类。同时额外设计信息查询器,对相关的数据进行查询和缓存,而设计的两个MyUmlElementMyUmlInterface类只担负信息专家的职责。

第二次作业

​ 由于这些图之间不具备直接关联,当第二次的需求到来之后,其实第一次的内容几乎完全不用修改。第二次作业用了相同的思路,My***Interaction类负责构造和查询信息的合法性判断,而MyUmlStateMachine类和MyUmlInteraction类两个状态机和交互图的顶层类拥有足够的信息,负责完成交互。这里给出状态机的简要类图作为示意。尝试过直接继承官方包的UML类,但因其构造方法为private操作上遇到了困难,所以仍然用关联关系构造MyUml类来保存额外的信息。除了Interaction外,所有UML元素都用这种方法进行重新封装,将需求的方法加入其中。

2

​ 但这样做的难点就在于这些类的初始化过程,由于层次结构多,需要层层初始化,感觉比较繁琐。这里省略了很多初始化时需要调用的方法,比如为状态机增加Transition,为Region增加State等方法均没有写出来。最终完成后总代码行数居然达到了1000多行,不过比较好的是最大的类不超过300行,类的数量达到了13个,功能基本做到了分散。

​ 之后再回想,这样多的类需要进行实例化,其实可以使用工厂方法,这里的StateChartInteraction实际上就隐含了这个功能。之所以没有专门使用工厂方法,是因为需要对输入的信息进行反复多次整理之后才能形成正确的层次结构,当这些结构形成的时候,似乎已经能够完整地表达这个图了(一堆HashMap容器表示关系),再构造自己的类看起来似乎有点多余。但因为数据和操作结合,是面向对象的基本思想,这样做也确实是合理的。当功能拓展后,如果拿着一堆HashMap再试图扩展,某个类里或许就会变得非常凌乱。

第三次作业

​ 本次作业的需求是加上部分UML语法检查,而其余功能未改变。根据Open-Close Principle决定以最小化改动原有代码为基础,设计出了Validator类专门负责语法检查。将检查分为两类分别设计了类图的检查器和状态图的检查器,在MyUmlClassModelInteraction类和MyUmlStateChartInteraction类中对这些检查器进行初始化,以便对其中的数据进行检查。以MyUmlClassModelInteraction为例给出类图。

3

​ 通过这样的设计方式,没有引入新的bug而只需要仔细检查Validator内部实现即可,保证了软件质量的稳定性。

架构设计与OO方法演进

​ 通过OO一学期的学习,一个很直观的感受就是对程序功能模块的抽象能力得到了很大的提升,直观反映出来就是写出来的代码修改和debug的时间逐渐减少。而这和用于整理思路和设计架构的时间的增多是分不开的,有了清晰的架构,编写起代码来就胸有成竹。而各种OO设计模式的应用,如工厂方法、生产者消费者模型等,也为功能的正确实现助力。每个单元都有新的设计思想与架构的学习、领会与应用。

第一单元

​ 这一单元训练的核心在于抽象与多态,这似乎也是我重构次数最多的一个单元,当时完全没有对未来需求预测的意识,设计出来的架构几乎只能恰好满足当前任务的需要。比如第一次作业,直接使用了常数项类完成任务,而ParserExpReader之间的配合也恰好只能分析常数项的输入。第二次作业加入了新的输入要求,就不得不大改ExpReaderParser来适应新的需求。其实从现在的角度来看,当时如果对Open-Close原则有一定的认识的话,设计架构时考虑到未来模块的加入,将ExpReader设计为多模块兼容的实现方式,就不用多次重构了(第三次也重构了)。但好在完全理解了求导作为一个接口方法实现的优越性,也在我的程序中得到了广泛应用。

​ 不过对于组合项的处理,当时没有能够抽象出来。因为当时理解一个式子就是线性的,没有按照树的模式去理解,自然也就没有理解组合项以及其可作为Differentiable接口的实现这一点。

​ 通过多次重构的惨痛经历,我深刻体会到了设计一个可扩展架构对于迭代开发的重要性,而层次化设计通过这次训练更是成为了习惯的一部分,之后的作业中也尽可能地实现层次化设计理念。

第二单元

​ 这一单元的设计核心在于线程安全与并发,通过这一单元的实践,将生产者-消费者模型以及订阅-发布模式应用到了实践中。这一次的作业接触到的多线程是全新的概念,设计起来还是具有一定的挑战性。

​ 这单元也介绍了Java的一些设计原则,例如SOLID原则,对之后也具有了很强的指导意义。但就如老师所说,代码写多了才会对这些概念有进一步的了解。其实当时这些名词也只是一个概念,当回顾的时候才发现这些原则的独到之处以及自己不遵循这个原则所额外付出的时间和精力。

​ 从架构设计上讲,有了第一单元的教训,我在第一次作业进行设计的时候就预测了需求,可能会加入多部电梯。于是从输入到电梯,我搭建了较长的桥梁。从输入线程出来之后,就到了托盘,托盘负责生产者-消费者线程安全的保障。托盘的另一边是分派线程,一开始就设计成了线程,为其提供了在生命周期内动态分派任务的能力,而电梯则只需要从分派器提供的队列中取出分派的任务去执行即可。当然这里再一次引入了线程安全问题,需要进行特殊维护。

​ 这一次注重了架构设计后,确实减少了很多重构工作,不过对类功能的修改仍然没有符合开闭原则,增加新的功能后对已有的类几乎都改了一遍。其实开闭原则在本单元作业中有可以实现的地方,但由于功能耦合度确实太高,对于现有实现方式确实较难做到扩展。比如一个类的功能本来就比较复杂,开闭原则就难以实现,而如果一个功能是由多个模块组成,当某方面的需求变化的时候,就只需要扩展对应的类就行了。虽然注重了架构设计,但架构到了第三次作业其实已经不堪重负,可能再扩展一些功能就需要重构了,因为一个类的职责已经变得过于繁重。所以某些时候,是否需要对架构调整以便更加方便优雅地完成任务也是需要衡量的问题,重构不是洪水猛兽,而是让程序能够更加适应需求(不过很花时间)。

第三单元

​ 这一单元的核心是JML建模语言的实践应用,训练了数据抽象的能力,从某种程度上也练习了之前的设计方法。初次接触JML,对它不是很理解的时候,数据规格完全用的是与JML规格相同的表示,比如说是数组,就用ArrayList。后面学习了数据抽象(以及被CTLE暴打)之后才明白了设计与实现的分离,对契约编程有了更深入的认识。

​ 架构方面则是后面才被逐渐感受的另一种抽象层次,其实在这里设计和实现也可以成为对应关系。比如给出了接口,在接口之下的实现完全取决于写程序的人自身,只要满足接口的规范即可。这在第三次作业中体现出来,我意识到再往MyNetwork类里面加入功能,这个类就要累炸了。避免将功能集中在一个类上,这个设计思路在这里体现出来了,当然也是因为我知道了数据抽象的真正含义。

​ 这一单元也引入了单元测试,从设计上就要更加考虑从测试的角度去思考即面向测试编程,在这方面我当时做的确实是不足的。不过构造了JUnit单元测试用例,之后会介绍到。

第四单元

​ 这一单元通过实际上手解析UML的方式,加深了对UML的理解。另外,众多元素的投入,也挑战着综合设计能力。考虑到类和方法的聚合,我将给出的UMLElement聚合成为了自己的类,拥有足够的信息,对外提供查询方法。新增功能的耦合度不高,成功运用了之前没用上的Open-Close原则来进行额外设计。唯一的遗憾是对自定义类的初始化不够优雅,在各个类和方法之间反复横跳才完成了一个如MyUmlStateMachine的实例构造。

​ 总的来说,这次的作业也让我看到了自第一单元以来的进步。从完全不重视架构设计,到注意设计架构,划分功能,保留可扩展性。这样的进步是肉眼可见的,虽然还是有这样那样的问题存在,但收获还是非常多的。

测试的演进

​ 测试和程序的编写是密不可分的,它保证了设计出来的程序的质量,同时在迭代开发的时候维持软件的指令,不会让用户拿到一个经常崩溃的辣鸡软件。(说的是不是我的程序,逃~)

第一单元

​ 第一单元的测试还停留在黑盒测试之上,由于给出了评测机的评测方式,用python搭建了一个评测机,生成各种数据测试程序的正确性。这个测试方式在互测的时候也让我快速定位到了房间内其他人的bug,黑盒测试是一种比较有效的控制手段,因为用户只关心输入和输出,通过黑盒测试可以基本保证测试过的输入范围内不会产生异常现象。但是黑盒测试有它的缺点,测试覆盖范围难以控制,构造数据有一定的技巧,同时不可能完全保证程序没有问题。

第二单元

​ 第二单元来到了多线程测试,多线程测试一直是一个比较困难的问题。不过我较为保守的设计(用了挺多synchronized)没有给我带来死锁问题,而多线程的暂停是个难事,于是debug只能靠print大法。而这种方法也应用在了测试上,通过输出一些中间变量看看是不是符合预期的情况,不过这样的手动测试也只能覆盖一些比较关键的点以及出了bug的点,对于是否有bug还是比较难查。

​ 同样的,在这种环境下还是试图引入黑盒测试。通过python + 管道定时投放输入,也基本实现了黑盒测试的要求,输出方面也进行了基本判断看看电梯有没有吃人。将这两种方式结合,也能从一定程度上保证软件质量。

第三单元

​ 第三单元的契约化程序设计引入了JML规范和JUnit单元测试,原则上讲只要符合JML规范就可以保证程序的正确性。这和之前的黑盒测试就完全不一样了,黑盒测试只能确认程序有问题而不能确定程序没有问题,而JML从逻辑空间上保证了程序的完全正确。这样的程序也最适合使用JUnit做单元化测试了,因为每一个方法的规格都给出,只需要测试整个规格的各种情况即可。同样的,单元化测试的特点是可以进行回归测试,当程序的需求改变的时候,大多数方法的规格实际上并没有改变尽管实现可能有变化。这时候只需要简单点一下测试,就可以自动测试之前积累的所有测试用例,保证软件质量至少不倒退。

​ 这一次就没有引入黑盒测试了,JUnit真香!通过这种方法也找出了自己的许多问题并修复,在后面的开发过程中,这些积累的测试用例也确实起到了作用,不用提心吊胆地改程序了。

第四单元

​ 这一单元的测试其实可用的方法挺多,可以使用传统的黑盒测试,也可以使用JUnit测试。因为没有了代码规范,这次没有应用JUnit,而是针对性地测试了一些特殊情况与通用情况。JUnit是一个积累的过程,如果软件迭代次数很多,修改比较频繁,构建它是非常有必要的。这次的设计由于临近期末,就只按照了黑盒测试的方法进行设计,考虑到迭代功能修改可能不会很频繁放弃了JUnit。但测试作为软件设计的一个必经之路这个观念通过这几单元的作业已经成为了我的常识。

课程收获

​ 我最大的收获正如这门课的名字一样,就是学习到了面向对象程序设计的思想。在此之前,程序就是一个个函数,一个个序列组成的,数据和操作结合是从来没有想到的。在接触了Java语言之后,作为这门语言的一个设计核心,通过这一个学期的应用,面向对象的思想逐渐在我脑中清晰了。

​ 面向对象的封装、继承、多态作为我设计的时候的核心,已经内化于心。数据通过这种方式组织起来,为大型软件的应用提供了便利,也更加符合人们对日常事物的认知。通过一学期的学习,这三种基本特征在程序的设计过程中得到了广泛的应用,也让我体会到了这样设计的巧妙之处。同时,抽象出一个数据作为一个类,保证其功能明确,职责分配合理,协作关系简单而正确。这些都是在课程中学到的。

​ 架构先行的思想,在逐次OO训练的锤打过程中,也已经成为了一个习惯。设计一个好架构,比快速写出一个能work没有bug的程序更加重要。现实世界的程序需求是不断迭代的,这和之前的习惯也不太一样,学习C程序设计的时候,一个题就是那样,做完了代码就不要了,怎么写无所谓。工程化的要求,让我们必须关注架构的设计,一个好的架构在需求的变化面前能够处变不惊。

​ 多种设计模式,为问题的抽象提供了高效正确的解决思路。如多线程的生产者-消费者问题,通过学习前人总结的设计模式,在自己的程序中应用,最大程度地避免了踩别人已经踩过的坑。另外SOLID原则,也为我们高效应对需求变化,保证程序质量提供了指导。

​ 测试是保证软件质量的核心,通过OO的中强测分离和互测机制让我掌握了许多测试的方法,也明确了测试在软件质量保证中的重要性。除了黑盒测试,还有单元化测试和形式化验证的手段。之前出现的改了这边那边出问题,通过测试就可以最大程度上检测出这些问题,从而在源头上进行修复,避免在实际应用中酿成大错(比如强测爆炸(误))。

​ 一些协作开发相关的知识,比如git的使用,check style等。保证了多人协作的时候,代码具有可沟通,可协作的特点。

​ 总之,通过一学期的OO学习和磨炼,对较大程序系统的设计和规范有了基本的设计能力,对面向对象的思想通过实践深刻理解内化,架构先行、测试兜底,协作开发基础。虽然整个过程充满了曲折,每周花在OO上的时间还是不少的,加上还有OS等其他课程,几乎学期内都处于连轴转状态。但付出就有收获,高强度的训练带来的也是大量的回报。努力通过了所有中测,可能在寻找bug的过程中充满痛苦和绝望。通过自己的测试,强测虽然不算完美,但也对我的努力做出了应有的回报。在互测阶段阅读了别人的代码,体会了不同的人独到的设计思路。这些都是OO给我带来的独特的记忆。

课程改进建议

​ 课上测试的严谨性和系统性可以进一步提高,其实这个在本学期后面几次实验的时候已经提升了很多了。但从体验上讲,还有很多细节需要琢磨,比如生产者消费者那块具体要求其实不是很清楚。虽然能够理解课程组是想考察我们是否掌握了这些方法,但一个具体的需求能够让我们在课上测试的较短时间内能够明确需要做什么,做到什么程度。

​ 许多理论课讲的设计方法和思路,可以提供更加具体的代码样例让同学们自己课下体会。比如讲到开闭原则的时候,当时代码量积累不够的情况下,我觉得除了修改程序本身似乎很难做到通过扩展类来实现新功能。如果能够结合这些理论知识给出相应的示例代码包,可以让初次接触这些概念的同学更容易理解。

​ 多线程方面,可以对如何加锁、在何处加锁重点教授lock相关的方法,而不是只介绍synchronized。在学习到这一部分的时候,其实OS还没有学到多进程程序设计,对加锁这块还是理解不深,不太敢自行加锁。如果进行额外的讲解可能可以更好地提升多线程程序的性能。

课上学习OO体会

​ 总体感觉上,其实OO这门课程还是挺适合课上教学的,在课上学习OO,从体验上讲和课下差别并不是很大。课上讨论环节大家也在微信群里进行讨论,询问问题,也能学到许多知识。而课下与实验,通过OO的课程平台进行实验,就几乎没有什么区别了。可能感受不太一样的是讲课的过程中,老师无法关注到学生的反应,但好在线上课程可以无限次看回放,不懂的地方看看回放,或者在群里提问也能解决。

标签:总结,OO,黑盒,程序,测试,设计,单元,Unit4
来源: https://www.cnblogs.com/stevezzy/p/13137755.html