OO第四单元总结
(1)总结本单元两次作业的架构设计
(2)总结自己在四个单元中架构设计及OO方法理解的演进
(3)总结自己在四个单元中测试理解与实践的演进
(4)总结自己的课程收获
(5)立足于自己的体会给课程提三个具体改进建议
1. 本单元作业架构设计
1.1 UML1设计
本次作业主要是实现一个UML类图解析器,可以通过输入各种指令来进行类图有关信息的查询,有关mdj
文件的解析包仍有官方提供,我们只需要对解析出来的UML各个构成要素进行存储与联系即可。
分析
对于一个一个UmlElement
,其各个属性含义如下:
- UMLElement
- visibility:该元素的可见性
- name:该元素的名字
- _type:该元素的类型
- _id:该元素的id
本次主要包含五种UmlElement
元素,如下。
-
UMLClass
- _parent:对应的是一个特定的值
-UMLAttribute - _parent:对应该属性所在的类
- _parent:对应的是一个特定的值
-
UMLOperation
- _parent:对应的是该操作所在方法的id
-
UMLParameter
- type:该参数的数据类型
- direction:方向,in-参数;return-返回值
-
UMLAssociationEnd
- reference:该关联端对应的类或接口的ID
- multiplicity:重复度,该关联端对应类或接口的实例个数
- aggregation:聚合,暂未使用
-
UMLInterface
- _parent:同UMLClass,对应的同一个特定的值
其中,umlClass与umlInterface可直接解析;attribute,operation,parameter 以及associationEnd 由于分别对应了相应的 类,类,操作,类或接口,故有可能会出现在相应parent的前面,造成空指针错误,因而需要在录入时,分多次遍历。
此外,本次作业还包含了三种元素之间的关系:
- UMLInterfaceRealization
- _parent:同source
- source:对应实现该接口的类的ID
- target:对应该接口的ID
- UMLGeneralization
- _parent:同source,对应子类ID
- source:对应子类ID
- target:对应父类ID
- UMLAssociation
- end1:对应其中一个UMLAssociationEnd的Id
- end2:对应另外一个UMLAssociationEnd的Id
至此,所有元素以及元素之间的关系已经一目了然,我们只需保存相应的元素即可,而关系本身则不需要保存。
架构设计
本次作业主要包含两个包:utils
和 elements
。 utils
实现了官方要求的MyUmlInteraction
类 。 elements
中均是为了更高的扩展性,基于官方包中的各个UmlElement
元素重写的各个 MyUmlElement
方法。文件树如下:
└─homework
└─uml1
│ Main.java
│
├─elements
│ │ MyUmlElement.java
│ │
│ ├─classifier
│ │ Answer.java
│ │ MyUmlClass.java
│ │ MyUmlClassifier.java
│ │ MyUmlInterface.java
│ │
│ └─meta
│ MyUmlAssociationEnd.java
│ MyUmlAttribute.java
│ MyUmlOperation.java
│ MyUmlParameter.java
│
└─utils
MyUmlInteraction.java
类设计
homework.uml1.utils
MyUmlInteraction
- 存储了
UmlClass
、UmlInterace
、UmlOperation
以及UmlAssociationEnd
。
- 存储了
homework.uml1.elements
MyUmlElement
- 继承了
UmlElement
- 重写了
equals()
以及hashcode()
方法
- 继承了
meta.*
- 实现了各个MyUmlElement
- 其中,
MyUmlOperation
需要保存其相应的MyUmlParameter
classifier.*
- 由于类与接口都需要存储相应的属性,方法,关联对端,故将其抽象为
MyUmlClassifier
。 Answer
类存储各类查询指令的答案,保证重复指令只查询一次,节省CPU时间与程序运行时间。
- 由于类与接口都需要存储相应的属性,方法,关联对端,故将其抽象为
其类图如下:
1.2 UML2
本次作业在上次作业的基础上,进一步实现了对UML顺序图和UML状态图的解析,以及增加了三个规则验证。
分析
UMLClassModel
同上次UmlInteraction。
UmlCollaboration
collaboration下可以有多个时序图交互
- 第一层次
- UmlAttribute(协作对象)
- _parent:所在的collaboration 我猜(与UmlInteraction相同)
- type:"" 我懵逼了
扮演Role1,Role2, ... 角色
- UmlInteraction(交互模型)
- _parent:所在的collaboration 我猜
- UmlAttribute(协作对象)
- 第二层次
- UmlMessage
- messageSort:消息类型(同步,简单等)
- _parent:所在的interaction
- source:消息发出者(lifeline)
- target:消息接受者(lifeline)
- UmlLifeline
- _parent:所在的interaction
- isMultiInstance:不懂
- represent:所代表的Role(UmlAttribute)
- UmlCombineFragment
- UmlMessage
UmlStateChart
- 顶层
- UmlStateMachine
- _parent:上一级
- UmlStateMachine
- 第一层
- UmlRegion
- _parent:所在的状态机(UmlStateMachine)
- UmlRegion
- 第二层
- UMLPseudostate(初始状态)
- _parent:所在Region
- UmlState
- _parent:所在Region
- UMLFinalState
- _parent:所在Region
- UMLPseudostate(初始状态)
- 第三层
- UmlTransition
- _parent:所在Region
- guard:表示该状态转换所能发生的时机(01阈值)
- source:源状态
- target:目的状态
- UMLEvent
- _parent:对应所在的transition
- UmlOpaqueBehavior
- UmlTransition
架构设计
本次设计延用了上次的架构,仍然主要包含两个包:utils
和 elements
。elements
补充了state
以及interact
等包,分别用于对状态图与顺序图的解析,文件树 如下:
└─homework
└─uml2
│ Main.java
│
├─elements
│ │ MyUmlElement.java
│ │
│ ├─classifier
│ │ Answer.java
│ │ MyUmlClass.java
│ │ MyUmlClassifier.java
│ │ MyUmlInterface.java
│ │
│ ├─interact
│ │ MyUmlInteraction.java
│ │ MyUmlStateMachine.java
│ │
│ ├─meta
│ │ MyUmlAssociationEnd.java
│ │ MyUmlAttribute.java
│ │ MyUmlLifeline.java
│ │ MyUmlMessage.java
│ │ MyUmlOperation.java
│ │ MyUmlParameter.java
│ │ MyUmlTransition.java
│ │
│ └─state
│ MyUmlFinalState.java
│ MyUmlPseudostate.java
│ MyUmlState.java
│
└─utils
MyUmlClassModelInteraction.java
MyUmlCollaborationInteraction.java
MyUmlGeneralInteraction.java
MyUmlStandardPreCheck.java
MyUmlStateChartInteraction.java
类设计
本次基于上次作业的架构重点实现了三个交互类,一个检查类,并将其耦合在一个MyUmlGeneralInteraction
中;同时基于需要,添加了几个相关的UmlElement
类,并适当修改了MyUmlClass
以及MyUmlInterface
类。
其类图如下:
2. 四个单元中的架构设计及OO方法理解的演进
第一单元 - 多项式求导
一开始,刚刚接触OO的我对于老师与助教不断强调的"架构"的理解懵懵懂懂,而程序设计与分析则更是停留在C语言面向过程的编程思维中。本单元作为一个供我们快速入门OO的基础篇,对于作业需求的逐层递进,我认为非常合理且十分有必要的,迫使我们在变幻莫测的需求与测试的夹缝之中,一步步朝着面向对象思维的转变。尤其是在第三次作业时新增的对嵌套函数的求导,可以说如果没有一个好的架构,是支撑不起这样复杂的Wrong Format的判断以及递归嵌套的求解的。
在本单元的作业中,我在第三次作业中第一次尝试使用了抽象类,这样做的好处是对于每个项的求导,我不再需要一个巨大的switch
来判断其到底是一类,从而进行强制类型转换后再调用相应的求导方法;取而代之的是,对每个abstract
类的元素,对其方法的调用,会自动向下转型为对相应子类方法的调用。
第二单元——多电梯调度
本单元第一次接触多线程,一路走来可以说是十分享(tong)受(ku)了,但多亏了老师在PPT中给我们提供的两种架构设计思路,减轻了我们的负担,同时也更好地帮助我们体会架构的重要性与优越性。
本单元最终架构还算说得过去,我对电梯的解析主要采用了 Producer-Consumer 与 Guarded Suspension 模式。主要有请求输入装置、主控制器以及电梯三个线程,难点在于对换乘请求的处理。但对于高内聚,低耦合的把控却不够,尤其是对C电梯的处理。由于迷之3楼只有C电梯能够上去,出于时间紧迫,我最终选择了枚举的方式,考虑了各种可能的情况,如"低楼层->3"、"高楼层->3"、"2->3"、"4->3"等等,大段的if-else
嵌套,即便有充分的注释,回头再看时理解与检查起来也十分的费劲。
第三单元——类地铁管理系统
老实说,本单元最后一次作业一开始的架构十分的差劲,出于种种原因,在ddl前一天晚上选择了重构,结果写了一堆*,强测结果可想而知。在后续bug修复过程中,尝试了"1+2"、"1+1"式拆点法以及各种可能的架构,总共重构了四次,每一次重构都有新的体会与感悟,一个优美的架构好处不仅仅在于其可扩展性,由于模块与模块之间耦合度低,逻辑重合度低,其bug调试及定位难度也大幅降低,同时也能更好的借助JUnit进行覆盖性测试。
第四单元——UML元素解析器
作为OO课程的最后一个单元,在阅读与学习了整整一个学期优秀的代码后,加上有充分的时间设计,本单元作业的最终架构还算比较满意,并且对可扩展性、耦合度、鲁棒性都有一定的考量,也算小有所成吧。尤其是从第一次到第二次作业的扩展,仅仅是在原有的基础上添加新的功能,而原有架构与功能也经历了强测的检验,从而在完成作业时也十分得心应手,更是让我体会到了好的架构的方便之处。
3. 测试理解与实践的演进
测试是软件工程中必不可少且可以说是最为重要的一个环节,一个良好的架构是对定点测试与bug修复的前提,对拍器是对大大提升测试效率的工具,而大量随机数据的投喂则是对软件质量的保障。
第一单元难点在于对各种WF情况的考虑。在一开始,无论是公测还是互测,我均是在肉眼debug,但主要还是集中在对WF的检测上。在第三次作业中借助python中的numpy模块以及大佬的数据生成器,终于实现了对拍,提高了测试效率,但由此也导致了一个问题:我不愿意再去花大量的时间与精力去琢磨对方的程序逻辑,而把希望全部寄托在大量的随机数据上。
第二单元则主要是线程安全以及电梯运行时间的把控。多线程的测试应该是最为头疼的了,虽然官方给提供了标准运行时间的解析包,使得TLE问题较为容易进行调控。但线程运行时的随机性而导致的线程安全问题,却大大增加了bug定位与复现的难度,从而导致难以调试,因而bug定位更多地仍然是从分析整个程序运行的逻辑上入手。
第三单元开始接触到 JUnit 单元测试,同时也学习到了基于JML的规格检查以及 JML Unit 工具的使用。此外,输出格式的固定与统一也使得正确性的检验较为容易,直接fc
即可,给自动化测试的部署带来了极大的便利。
第四单元由于数据是基于StarUML的,因而难以生成大量随机数据进行测试,但其对于 JUnit 单元测试仍旧十分友好,在本单元的测试中,我仍旧重点采用程序逻辑分析法,对每条查询指令以及每一类规则检查都尽可能地进行了覆盖性检查,如下是我对R003
的分析:
/**
* 检查UML009 规则
*
* 重复继承的情况最为复杂,需要检查是否有
* 0. 类重复继承
* 由于不支持类的多继承,故类的重复继承体现为循环继承
* 1. 接口重复继承
* 2. 类实现了 "重复继承" 的接口
* 2.1 一个类直接实现了某个 "重复继承" 的接口
* 2.2 一个类的父类实现了某个 "重复继承" 的接口
* 3. 类实现了 "重复" 的接口
* 3.1 一个类直接实现了两个 "重名" 的接口: C1 implement I1, I1
* 3.2 一个类实现了一个接口和这个接口的父类: C1 implement I1,I2 && I2 extends I1
* 3.3 一个类与其父类都实现了同一个接口:
* C1 implement I1 && C2 implement I1 && C2 extends C1
* @throws UmlRule009Exception 规则异常
*/
"测试只是为了尽可能地发现bug,而通过测试并不意味着你的程序就没有bug了"。吴际老师的这句话值得我们去好好反省与深思,我们总是因为过了中测而沾沾自喜,而当强测结果出来时才懊悔不已,痛恨自己如果当初好好做一下测试就好了。可是现实没有如果,假若把这些低级错误,这种侥幸心理放在未来的工作中,很可能迎接你只有一句"YOU ARE FIRED"。
4. 课程收获
一学期的OO之旅终于到了终点,这期间踩了不少坑,也吃了不少苦头,但从零基础一路走来,真的能感受到自己的巨大进步。在第一单元,我学会了正则表达式的使用以及OO的基本思想如类,接口,继承等;第二单元则学会了多线程程序的设计,包括线程安全的人为把控,以及SOLID设计原则等;第三单元学习java的建模语言,意识到了设计与实现分离的重要性以及必要性,同时初步体会到了网络流+拆点的精妙之处;最后一个单元的练习更像是一个总结,不仅是对更广义上的面向对象建模设计UML模型的理解,还是对前几个单元所有学习到的知识的一个汇总,整理以及实际运用,都令我受益匪浅。此外,老师及助教也不断强调要严格遵循代码规范,培养良好的编码风格,不能每次都依靠CheckStyle信息去调整,经过一个学期的磨炼,我的codestyle也有了很大的改进与提升。
总之,一学期下来,确实能感受到自己己的能力在稳步提升,也逐渐从面向过程的思维中转变了过来,但距离一个优秀的架构师仍有很大距离,我仍然需要大量及深入地通过阅读专业书籍以及模范代码,学习更多更巧妙的设计思想,逐渐形成一套自己的思维体系。
5. 一点建议
- 我认为可以适当减少互测屋的人数,在OO与OS两座大山前,我们很难有足够的精力去把同屋其他7个人的代码都认真研读一遍,更不用说体会其中的精妙,同时找出逻辑bug了。我想,大部分人都会选择部署自动化工具,进行随机测试,如果没有跑出bug,那就随缘...
- Bug修复阶段,对于非合并修复驳回的说明过于简略,且过于晚... 希望不要让认真修bug的同学心凉...
- 关于Bug修复的接口可以一直保留,便于我们回看并总结自己的bug。
- 关于网站,希望可以有侧边的滑动栏... 不然每次都要滚轮滚好久好久...
最后,在此衷心感谢OO课程组的老师及助教们,你们辛苦了,真心祝愿OO课程越来越好!