本文作为OO的最后一次博客作业,主要回顾了第四单元的架构设计和本学期的心路历程。
本单元架构设计
UML1
第一次作业的主要内容是解析mdj格式输入,记录特定数据并支持针对类、属性和方法等的查询功能。
我按照层级关系为类、属性和方法等对象设置了类,在UMLInteraction中解析输入的UML element元素,首先解析类和接口,以id为key,MyClass/MyInterface对象为value将它们组织在HashMap中,再依次解析其他元素将它们存入已有的MyClass/MyInterface实例中。对于关联关系,在MyClass中设置了一个assoList用来存储关联的对端的id。对于继承关系,在MyClass中有一个MyClass的引用指向该MyClass的父类;在MyInterface中有一个HashMap记录接口继承的全部接口。
这样组织数据结构使得大部分方法的实现可以通过递归来实现:先在类中查询,当父亲不为null时递归调用父类的查询方法。
此外,为了处理同名类、同名属性的异常,我在MyClass和MyAttribute等类中都使用了HashMap记录各名称出现的次数,并在查询时进行核对。
值得一提的是,在类/接口中设置指向父亲的引用,是后来受人点拨才有的改进。一开始我想把类设置成类与类之间互不透明的,全部跨类的操作都需要在UMLInteraction中进行。其实里的“低耦合,安全性”并无太大必要:父类的大部分数据,子类是可以进行访问的。此外,这样还增加了类的存储空间占用:一个类必须存储自身的和继承来的全部属性和关联,存在同一数据被存储多次的情况。
UML2
第二次作业增加了对状态图、顺序图和检查规则的处理。
对于状态图的处理,我采用了类似组织MyClass的方式:解析stateMachine—解析Region—解析state—解析transition。状态数、转移数可通过对stateMachine中的相关类进行计数来实现,后继状态数则需要使用广度优先算法,判断从当前状态出发能到达的状态数。
对于顺序图的处理类似:建立MyInteraction的列表-解析LifeLine-解析Message之后再进行查询。
三个检查规则是本次作业中较难的部分。对于规则001,只需统计属性和关联对端中所有名称出现的次数即可,当存在名称出现次数大于1时,需要抛出异常,传递所有重复出现的名称。对于规则002,需要考虑类继承和接口继承中成环的情况。我使用了深度优先算法得到类和接口的继承链,当下一个节点已经在链中存在时,停止搜索并返回继承环。对于规则003则采用深度优先算法得到所有继承的对象及其出现的次数,当某个类/接口继承的对象中有对象出现次数超过两次时,返回该类/接口。
为了使层次结构更清晰,我对上次实现的数据结构和方法(与本次功能独立)进行了封装。
架构设计和OO方法理解的演进
1)架构设计
架构设计方面的演进主要体现在类数量的增多、类复杂度的降低以及更多地采用设计模式。
第一单元结束后,我学会了按层级组织类,将复杂的功能分解为类与类之间的协作。第二单元则让我尝到了设计模式的甜头,working-thread模式让我不必为线程安全问题而烦恼。第三单元图的处理,由于新增功能与原有功能独立,我开始更多地进行封装,并做了继承的尝试。面对第四单元大量的数据类型,我开始更加有条理的组织类和类之间的协作关系。
2)方法理解
OO的三大原则说起来很简单:继承、封装和多态,但是真正理解这几个字眼,是要靠动手实践的。
我对OO方法的理解,一开始是“封装相似功能的数据结构”,于是乎前几次作业我写的程序类内部复杂度都很高,类间关系主要是调用关系,相当于还是从前C语言的思路,只是把同一类的函数封装出去成为类了。我还大量使用了static方法以方便我的“函数调用”。
到了第一单元的最后一次作业,由于过程非常复杂,我决定用递归处理函数求导问题,所以很现实的问题就是我必须按层级组织不同的对象:表达式、项、因子。在这样组织的过程中,我领会到了“将复杂步骤分解为数个较为简单的对象之间的协作关系”这样的思想。
第二单元的电梯则让我对OO的理解更进一步,由于担心自己乱写会产生线程安全问题,我谨小慎微地采用了课程组推荐的设计模式,输入处理—调度器—电梯的架构成为了我写作业的指导思想。在实现这一架构的过程中,我意识到OO的设计思想,是先分离出设计要求中的逻辑对象,再按对象之间的协作关系组织程序,而不一定是先把复杂的步骤分解成对象之间的作用关系。
第三单元、第四单元,程序的数据类型和功能都很多,这使得我写作业的出发点不再是“程序要怎么做完这件事”,而是“需要哪些对象?实现功能需要哪些对象之间协作?”我开始认识到需要按程序逻辑组织对象,并先规定接口,随后实现。
测试理解和实践的演进
阶段一:手造数据
此阶段包括第一、二单元手造数据测试自己和其他人的代码,也包括第一单元仔细阅读其他人的正则表达式然后精心构造反例。
手造数据的问题是效率太低,且不能有效暴露自己代码的问题。
阶段二:黑盒测试
此阶段主要是靠数据生成器和对拍,利用随机数据暴露出程序问题。优点是生成数据更全面、高效,缺点是可能无法暴露代码的所有问题,且无法预料非法输入是否被正确处理。
我在第二、三单元基本上处在阶段二。
阶段三:UML检验
此阶段可采用OpenJML直接从形式上验证程序正确性,比测试更能暴露问题。缺点是对JML的书写有一定要求,且OpenJML语法略微不方便。
阶段四、单元测试
使用过一次,相比黑盒测试针对性更强,组织条理清晰。在分支全覆盖的情况下能保证代码正确。
课程收获
- 代码风格优化
- 架构设计更清晰、层级分明
- 开始以面向对象的思想设计程序
- 程序工具的自学能力
- 多线程编程与调试
- 了解和参与规格化设计
git使用技巧- 自动化测试工具、单元测试
课程建议
1)
有时强测炸了但是一次合并修复就修好了(当然可能是因为犯了比较基本的错误),建议进一步改进强测评分标准,或者增大强测数据集随机性。
2)
研讨课实际体验不佳,分享同学讲的快/笼统,学不到什么东西;台下反馈少,让人感觉如果拿不出干货就会被鄙视;如果本单元表现不是非常出色,完全不敢报名。
综合体验:“热闹是他们的,我什么都没有”。
希望能改变研讨课的现有模式。
3)
建议提升授课内容,制作更精良的PPT。现在的课件有点像概念的堆砌(缺少注释),实际可读性一般,感觉和作业的关系也不大。此外,在第三单元第四单元(尤其是第四单元),理论课内容和作业内容有一点点分离。