OO第四单元作业
BearHuchao 2020年6月16日
应该说这一单元讲的内容,是2013年OO课程改革前的教学内容核心,如果你只想当一个码农,了解下就够,如果你还想当项目经理、业务主管、CTO或者再高,那么需要花花心思。但是本单元的教学内容看着不难,难在刻骨铭心地自觉性思维。
我在看大家博客时,在UML图上基本都是copy出来往上一贴,几乎没有分析自己设计的深度思考的,没有一个人对比着前后电梯的UML图来说明自己的设计改进的。
希望大家在这一单元学习的时候,不妨拿出之前作业,对着所学内容重新思考下。——诸彤宇
前言
本单元的主题是UML。UML作为建模语言中的集大成者,是一种开放的方法,用于说明、可视化、构建和编写一个正在开发的、面向对象的、软件密集系统的制品的开放方法。UML展现了一系列最佳工程实践,这些最佳实践在对大规模,复杂系统进行建模方面,特别是在软件架构层次已经被验证有效。本单元的作业重点在于对UML图的解析以及如何从导出的UML图中获取相关信息并建立起不同模型图元素的继承关系。
第四单元相关内容
需求分析
对解析好的UmlElement
进行操作,通过标准输入指定操作来对相关内容进行查询。
解决思路
思路一及其实现
直接对相关的UmlElement
进行分析,通过UmlElement
的parent
,source
,target
等字段进行查找。
实际实现中,在对复杂元素进行重复递归时效率低下,并未采用。
思路二及其实现
通过对UmlElement
进行进一步解析,进而构建出相应的继承模型,使用新的包装类来构建出子类和基类的相关关系(即对UmlClass
,UmlInterface
等类构建出相关的类)。
实际实现中,在对相关的类进行优化后,能够有较好的效果。
结构分析
由于后两次作业都是在前一次作业的基础上进行功能的添加,因此对作业代码的结构分析将集中于第三次作业的相关内容中。
UML类图,强烈建议点开看大图
本次作业工程量较大,各包装类都继承了公共的父类Element
,对应UmlElement
。
在实现中,我将类分为基础类和非基础类。基础类会被存储在对应的集合中并在之后的查询中被直接调用;非基础类会以成员变量的形式存储在基础类中,由基础类进行调用,或者那些表示关系的类会直接被转化为关系间的相关关系。下面的表格则给出了更详细的对应关系:
UmlElement (原题类) |
Element (包装类) |
基础类? | |
---|---|---|---|
UmlInterface | Interface | 是 | 这个类中包含了大量适用于Class 和Interface 的相关方法。同时也存储了 Class 和Interface 的相关关系:继承关系、实现关系、关联关系等。另外还存储了类属性相关的内容,例如 Attribute 和Operation 等。 |
UmlClass | Class | 是 | 在实现中继承了Interface 类。 |
UmlAssociationEnd | AssociationEnd | 否 | 存在于Interface 中,对应指向自身的AssociationEnd 和指向其他类的AssociationEnd 。 |
UmlAssociation | Association | 否 | 关系类,在调用对应构造方法时通过其对应的AssociationEnd 被直接转化为类的相关关系,存储为HashSet<AssociationEnd> |
UmlAttribute | Attribute | 否 | 存在于Class 和Interface 中,对应类和接口的相关属性。 |
UmlGeneralization | Generalization | 否 | 关系类,在调用对应构造方法时通过其对应的target 和source 被转化为类的相关关系,存储为superClass 或者superInterfaces 。 |
UmlOperation | Operation | 否 | 存在于Class 中,对应该类拥有的操作。 |
UmlParameter | Parameter | 否 | 存在于Operation 中,对应该操作对用的所有参数。 |
在状态图的实现上,我采用了类似的方法,详细实现见下表:
UmlElemet | Element | 基础类? | 备注 |
---|---|---|---|
UmlStateMachine | StateMachine | 是 | 本实现中将Region 的部分功能转移到StateMachine 上。例如存储State 等出现在Region 中的内容。 |
UmlRegion | Region | 否 | 本实现中更多的是一个桥梁作用,将其中的内容直接存储在StateMachine 中,便于之后的查找。 |
UmlState | State | 否 | 存在于StateMachine 中,同时拥有并维护nextStates 成员变量。 |
UmlFinalState | State | 否 | 同上,但涉及到R007的判定 |
UmlPseudostate | State | 否 | 同State ,但涉及到R008的判定 |
UmlTransition | Transition | 否 | 关系类,在调用构造方法的时候会被转化为对State 类nextState 变量的维护。 |
对顺序图的实现也采用了类似的方法,如下表所示:
UmlElement | Element | 基础类? | 备注 |
---|---|---|---|
UmlInteraction | Interaction | 是 | 存储了Lifeline 、Endpoint 、Message 等相关类。 |
UmlLifeline | Lifeline | 否 | 存储了相关的inMessage 和outMessage 。 |
UmlEndpoint | Endpoint | 否 | 本实现中直接继承了Lifeline 。 |
UmlMessage | Message | 否 | 直接存储在Lifeline 或Endpoint 中。 |
本次作业虽然种类繁多,但是在深层中有极强的连通,因此三次作业通用了一套框架。
在最后一次作业中由于出现了对模型有效性的检查,因此增加了图论中的相关内容。下表给出了不同的规则及其解决办法:
规则 | 实现 | 备注 |
---|---|---|
不能含有重名的成员 | 直接在Interface 类中添加了checkRule001() 方法来对本类的成员进行检测并返回对应的Set<AttributeClassInformation> 。 |
主类中添加了相应的errorSet。 |
不能有循环继承 | 使用DFS+Stack进行搜索,将存在于环内的元素直接存储于类静态变量rule002Set 中。 |
|
任何一个类或接口不能重复继承另外一个类或接口 | A满足条件,当且仅当(n(S'(A)) = 1 + sumlimits_{x in D(A)} n(S'(x)))。 不满足的类被存储在静态变量 rule003Set 中。 |
(D(x)) 为 (x) 直接继承的所有对象 (S(x)) 为 (x) 直接或间接继承的所有对象,(S(x) = igcuplimits_{y in D(x)}{S(y)}) (S'(x) = S(x)cup{{x}}) |
任何一个类不能重复实现同一个接口 | 实现与上个规则类似。 不满足的类被存储在静态变量 rule004Set 中。 |
注意调用成员变量时的作用域 |
类图元素名字不能为空 | 在每次将UmlElement 转化为包装类的时候对类进行检查,如果类名为空则直接将静态变量置真。 |
|
接口的所有属性均需要为public |
在向Interface 中添加属性时进行判断即可。 |
|
Final state 不能有状态迁出 |
在处理Transition 类时进行判断即可。 |
|
Initial state 最多只能有一个状态迁出 |
在向State 添加转移时判断即可。 |
架构设计及OO方法理解的演进
第一单元是整个OO作业中最难的一部分。由于涉及到复杂的递归处理,导致我在第一次作业提出了对正则表达式进行标准化处理的方法失效,因此对作业进行了数次重构。第一次作业的架构进行了数次调整。同时由于优化问题的存在,本次作业成为了最内卷的一次。
第二单元是OO作业中最玄学的一个单元。由于涉及到多线程,因此调试起来异常困难。本单元后面的作业基本都是在第一次作业的基础上进行改进,因此总体来说本单元的架构设计较为合理。
第三单元是OO作业中比较简单的一个单元,只要理解了JML语义,实现起来算比较简单。从本单元作业开始,涉及到了图论的相关知识。总体来看,本次作业涉及架构部分不算多,更多的重点存在于算法上。
第四单元的相关内容在前面的文字中已经进行了详细的阐述,在此不再赘述。
面向对象编程的思想在历次作业中体现得程度不同,个人感受是在第一单元的字符串抽象为多项式。之后对多项式进行的多张操作充分展现了面向对象的方法。第二单元在多线程的过程中充分体现了将线程作为对象进行管理这一方法的便捷性。第四单元中对包装类的管理也体现了这一特点。
田韵豪同学在其研讨课上展示了Stream
的用法,本人接触后受益良多,在进一步询问下得到了进一步指点,在本次作业中大量运用这种写法,感谢!
测试理解与实践的演进
在历次作业中都需要对代码进行测试,但是奈何本人很懒,而且要和助教一起快乐游戏,因此测试大多采用的是手动构造针对性的样例。
历次测试中,我特别感谢学习群中的各位同学,他们分别是:
王雨轩、杨祎然、彭冠涵、刘仪、曹文轩;
以及田韵豪和何林璇两位同学。
由于数次疏忽,本课程中有几次作业的成绩不尽理想,这其中最严重的莫过于第一单元第三次作业,由于在输出的过程中少写了括号,结果就是:
第三单元JML的测试虽然进行了不少,但是还是在强测中错了一些显而易见的测试点(漏判了1111)。
测试不是万能的,老老实实写代码才是王道。
课程收获
在本学期的面向对象编程课程中,我学习了相当多的面向对象编程知识,初步掌握了Java语言这一强大的面向对象工具 。同时,在本课程中第一次正式学习了传说中的(玄学)多线程。JML也提供了一整套对方法进行形式化设计及验证的工具链。(虽然验证工具极度原始简陋,且大大落后于时代)最后一个单元从总体的角度来对整个项目进行模型化的设计,从类和类的成员及它们之间的关系,到某个方法或者调用过程中状态的改变,再到不同的类、不同成员之间通过调用返回等消息如何进行协作等等。
总的来说,收获颇丰!
(不过肯定没有t123yh收获丰厚)
具体改进建议
- 实验课建议改革,最起码是让同学们能够了解到自己每次实验的结果如何,而不是每次在那里打哈哈看总体分布图猜自己在什么位置
(你是什么成分?)。另外,实验课助教回答问题总是很模糊,不知道是不能说还是在阴阳。“每次同学们纠结的东西在总评中可能都占不到1分,但是助教们还是尽力在解答”(?我寻思题目不清楚助教是受害者咯?总评不到1分累计起来是多少?特别是在6系整个都在内卷的大环境下这样的疏忽应该大量存在吗?) - 对每次作业的指导书,建议多找人验题,不要总是照搬以前的指导书,助教们对着去年不清不楚不明不白云里雾里的指导书不会破口大骂吗?拿着这些指导书去问助教助教不给准确的回复还在那里阴阳怪气不难受吗?课程组应该确保下发文件的权威性,朝令夕改会出现大量的问题。
- 课程教授内容同作业不完全符合的问题。在JML单元,重心大多放在了读懂JML来完成对相应代码的构建上而不是JML所代表的形式化验证手段的使用上;UML单元着重于在给出Uml元素后对其中的相关关系进行检查而没有真正做到使用这一语言来进行面向对象的构造。(这部分主要集中在相应单元的实验中)这两个单元算法和数据结构都占了比较重要的部分。
线上学习的体会
咱们这一届可能是第一届(也有可能是最后一届)完完全全在线上进行OO课程学习的学生了。老师和助教都尽心尽责,非常感谢他们从寒假一直持续到暑假的辛勤付出!
革命尚未成功,同志仍需努力!希望OO课程组能越做越好!
感觉学习了一整个学期,整个课程改名叫Java程序设计和数据结构也没太大问题
全篇都是肺腑之言,希望老师和助教客观公正看待。