面向对象第四单元总结

面向对象第四单元总结

一、第四单元作业架构设计

第13次作业

本次作业需要实现类图解析。总体思路是新建多个类表示类图中的元素,根据类图元素之间的层次关系构建类之间的关系。ClassInterface是最高级别的元素。Class包含OperationAttributeInterface包含OperationOperation包含Parameter。最底层元素可以直接使用官方包的Uml_*类表示,自己定义MyClassInter, Operation三个类。类图如下:

例如,MyClass的属性father表示其父类,realization容器表示实现的所有接口,attr容器表示类的属性,ops容器表示类的所有方法。InterOperation类的设计也类似。继承、实现、关联关系在MyUmlInteraction中解析出来并添加到MyClassInter中。

另外,对于作业要求实现的各种查询操作,我大多采用下降到自定义的各种类中实现,减少MyUmlInteraction类的复杂度。

第14次作业

本次作业新增了状态图和顺序图的查询。作业中的顺序图和状态图与类图相对独立,思路与上一次作业类似,定义自己的类表示顺序图和状态图中的各种元素,将查询操作下降到各个类中实现。定义状态图元素StateMachineStateTransition和顺序图元素InteractionLifeline

一个StateMachine表示一个状态图,相当于一个带权有向图,用State管理与之相关的Transition。查询可达状态的操作比较耗费时间,为此,我在State中用linked保存了其所有可达状态,在添加状态转移关系的时候更新linked容器。最终查询可达状态返回linked容器即可,提高效率。

顺序图要求相对简单,各个Lifeline保存各自发送和接收的UmlMessage即可。

本次作业类图如下

第15次作业

本次作业架构和上次作业相同,新增了正确性检验。

本次难点在于循环继承、重复继承、重复实现,这些操作均可归结为有向图的查询。将类、接口视为结点,各结点保存其所有子结点和父结点(包括间接父结点和间接子结点),在添加有向关系的时候增加父子结点。若新增父结点,已将在子结点的孩子集合中,则次关系添加后产生循环继承。在构建有向图的过程中记录个各类是否有循环继承、重复继承、重复实现,检查有效性的时候不必遍历整个图,避免了相对复杂的图查询算法。

二、架构设计和OO方法理解演进

第一单元

第一单元重点在于层次化设计,需要从表达式中分离出不同层次的元素,并定义相应的类来表示。

  • 在研讨课上,我了解到了表达式解析的递归下降法,它形象地表示了表达式的组成,并用递归来实现了复杂嵌套表达式的求导,易于理解。
  • 完成本单元作业的过程中我经历了重构,充分体会到OO作业中架构设计的重要性。在编写代码之前,我们首先要对程序架构有一个相对清晰的构思,良好的架构能提高编写代码的效率,减少出现bug的可能性。所以,编程前期的架构设计是非常必要的,所谓“磨刀不误砍柴工”。

第二单元

第二单元重点在于多线程的协作。这是我第一次接触多线程编程,本单元第一次作业就耗费了大量的时间。

  • 经过本单元的训练,我熟悉了使用共享对象实现线程交流、合理设置临界区保证互斥访问和维护线程安全的基本方法。本单元第一次作业效果不理想,原因首先在于架构设计的不合理,其次在于临界区过大,把很多可以并发执行的代码放到了临界区内,降低效率。后来我采用了三组线程(输入线程、调度器、电梯线程)和两组缓冲队列结合的架构,将各个模块的功能独立出来,同时共享对象用完即解锁,增加了并发度。
  • 除了多线程架构设计,第二单元的另一个难点在于调度算法的设计,我按电梯容量将请求分组打包发送,同时考虑组内捎带。当时考虑到换乘未必能提高效率,所以我没有写换乘的算法(其实我们的作业设置的天体特征还是倾向于换乘的,应该尝试一下)。调度算法也是一个很有价值的知识点,各种计算资源的分配,物流配送都涉及调度,应用很广泛。

第三单元

第三单元对架构的要求有所降低,关键在于按规格编写代码。在第二次作业中我看错了一行规格描述,导致了严重的低级错误,受到了深刻的教训。虽说本单元是按规格写,但不等于抄写规格。本单元对性能有较高的要求,尤其是与图算法相关的部分,我们需要在读懂规格的基础上尽量选择最优化的算法。我在性能上也有不少损失,这也让我认识到规格只是用来描述功能的,在具体实现上我们可以有很多自己的优化方式。

第四单元

在架构设计上,我认为重点在于先理解UML模型中的各种元素的含义及其层次关系,然后再构建类来表示。各个对象独立的查询操作应该封装到底层来实现,顶层的MyUmlInteraction类只负责调用底层的方法。这样做能使结构层次更清晰,符合面向对象的思维方式。各种层次的元素之间的组成关系可以表现为树形结构。从这个角度看,本单元的架构设计与第一单元有相似之处。

三、对测试的理解和实践演进

通过一学期的面向对象课程学习,我越来越觉得测试非常重要!!!我在测试上主要经历以下几个阶段。

1.手动构造数据

我认为手动构造数据对边界条件测试是比较有效的,相比之下,随机自动生成的数据反而未必能涵盖很多边界情况。例如,第一单元的表达式求导就需要考虑很多种特殊的表达式。手动构造一些简短但形式特殊的数据很容易发现bug。不过这也需要我们充分考虑,站在一个hacker的角度想出各种刁钻的测试用例。

2.自动构造数据

显然,手动构造产生的数据量很少而且耗费时间,对于需要用大规模数据做性能测试的情况不太适用。我在第二、三单元尝试了一些简单的自动生成数据的方法。 例如,第三单元的社交网络查询指令有固定的格式,适用于随机生成数据,用python可以方便地实现。我把指令分为了几个类别,分模块来生成。可以通过调整参数来改变各类指令所占的比例以及非法指令所占的比例。

自动生成的数据数量多,覆盖面广,但有时候难以生成一些特殊的样例。在自动生成的基础上手动添加一些特殊样例可以显著增加数据的强度。

3.对拍

在第三单元的测试中,我使用了自动生成加对拍的方法。对拍的关键还是要生成有足够强度的数据。像第三单元这样的单线程程序,输入与输出经常是一一对应的,这就使得对拍变得比较容易,起始就是一个简单的文本比较。

不过,测试再充分也不能证明程序没有bug。在对拍中我也遇到同时出同一个bug而没有发现的情况。我觉得应该坚信程序必然有漏洞,培养自己的黑客思维,也要积极参与互测,这样有助于提高debug的能力。

四、课程收获

1.养成了面向对象的思维方式

  • 经过一学期的训练,我习惯了用面向对象的方式来思考编程问题:将问题中的参与者划分为多种不同的对象,思考各自应该实现的功能,这对于拆分复杂问题,解决大型项目很有帮助,也为我们日后参与真正的软件开发奠定了基础。
  • 我认识到了封装的必要性。面向对象语言的类是一种将数据和操作封装在一起的数据结构,能很好的反映了现实世界中的事物。将具体的数据和方法隐藏到底层,而在顶层仅提供对外的接口,这不仅更符合现实世界的特征,也能降低代码的耦合程度,便于维护。第一单元的递归下降和第四单元的分层实现都体现了这种思想。只有养成封装的好习惯,才能更适应未来合作开发的场景。

2.认识到架构设计的重要性

架构设计是本课程贯穿始终的内容,我对此也深有体会(前两个单元初期的不良架构带来了很多麻烦)。在以往的编程练习中,我们的代码往往只是一个孤立的程序,不是一个工程,当然也不需要增量开发,所以完全可以把所有代码都放在一个文件中实现。而面向对象适应的是大型项目的开发,整体骨架往往比具体细节更重要。清晰的架构不仅易于自己维护和迭代,也便于别人阅读和交流。在面向对象设计的过程中,我让自己更关注于宏观的结构而非微观的算法。有位老师曾说过,不要在开始写代码之前过度考虑优化问题。其实我认为这里的优化问题就属于微观细节,它与整体架构是相对独立的,我们搭好框架之后完全可以在各个局部继续完善细节。如果在一开始思考过多的底层细节反而容易导致架构混乱,顾此失彼。

五、课程改进建议

  • 建议在pre部分增加一些多线程编程的入门知识讲解。在以往的学习过程中我们很难接触到多线程的内容,如果没有多线程基础,第二单元作业开始阶段会比较吃力。
  • 建议第四单元给出更详细的UML指导书,系统地介绍一下UML模型各种元素及其相互联系(网络上能找到的UML的相关知识比较有限和零散,给出详细的指导有助于加深对UML的理解)。
  • 建议适当加强中测数据,便于更好地发现一些低级错误。
posted @ 2021-06-26 16:24  李雨东  阅读(88)  评论(0)    收藏  举报