面向对象程序设计第四单元总结
UML
第四单元学习的内容是UML (Unified Modeling Language)
主要学习了三种图:类图(class diagram), 顺序图(sequence diagram), 状态图(state machine diagram)
类图
类
类图分三层,第一层显示类的名称,如果是抽象类,则就用斜体显示。第二层是类的特性,通常就是字段和属性。第三层是类的操作,通常是方法或行为。注意前面的符号,‘+‘ 表示public, ‘-‘ 表示private,“#‘ 表示protected.”
接口
与类类似, 在staruml中用圆圈表示
六大关系
继承关系(Generalization)
也称泛化关系
可以简单理解为继承, 在uml中用空心三角箭头表示
java中类不可以多继承
实现关系(Implementation)
类实现接口的关系, 在uml中用虚线加空心三角表示, 在staruml中就是一根直线(也叫做interface realization)
一个类可以实现多个接口
关联关系(Association)
类与类之间的联系:关联的本质是一个类需要另一个类来管理数据或者请求服务
关联关系是一种拥有的关系,它使一个类知道另一个类的属性和方法
在staruml中是一根直线, 一般是一根实线箭头(单向)
聚合关系(Aggregation)
聚合关系表示的是整体和部分的关系
整体由部分组成,而整体和部分又可以分开, 在staruml中用空心菱形和实现表示
组合关系(Composition)
整体与部分的关系,但是整体与部分不可以分开, 一般用实心菱形加实线表示
依赖关系(Dependence)
只要是在类中用到了对方,那么它们之间就存在依赖关系。如:类的成员属性, 方法的返回类型, 方法接受的参数等。
staruml中用虚线箭头表示
顺序图
协同
UmlInteraction用于表示对象的交互
参与者(participant)
参与者描述本次交互的发起者, 在作业中主要用到的包括两类
生命线
UmlLifeline, 生命线用于描述对象的生存周期, 在uml中一般用方块加一条虚线表示
终点
UmlEndpoint,顾名思义就是结束点, 一般用实心原点表示
消息(Message)
UmlMessage, 对象之间通过发送消息来进行交互, 不同的消息在staruml中的表示方法也不一样
六种消息类型
没有重点考察,只是大致了解
同步消息
(sync message) 或者 (self message)
一般就用实线箭头表示。 一个对象向另一个对象发出同步消息后,将处于阻塞状态,一直等到另一个对象的回应。
异步消息
一个对象向另一个对象发出同步消息后,将处于阻塞状态,一直等到另一个对象的回应
一般用普通箭头表示
返回消息
一个对象向另一个对象发出同步消息后,将处于阻塞状态,一直等到另一个对象的回应
返回消息用虚线箭头表示
创建消息
注意生命线的变化
删除消息
丢失消息
消息指向一个黑点
找回消息
从一个黑点指出消息
状态图
主要用到的几个概念
状态机(State Machine)
状态图最顶端的部分, 状态机整体
区域 (Region)
状态机下面的子部分, 用画布的概念理解
引用吴际老师原话
不直接在StateMachine下直接加状态的原因:StateMachine 中除了state之外还可以增加其他的内容,在staruml中可以发现还可以加constraint和tag。 这样在StateMachine和State之间加入Region可以有更好的层次。
状态 (State)
区域下面的概念, 正真表示状态机内的状态
起始状态(UmlPseudostate)
个人感觉pseudostate和初始状态Initial State还是有一点区别, 但是在作业中就当起始状态用了
普通状态(UmlState)
终止状态(UmlFinalState)
结束状态
转移 (Transition)
或者说状态转换:两个状态之间的关系,它表明当某事发生时,对象先从当前状态转换到后来的状态,用带有标记事件的箭头表示。
事件 (Event)
表示状态转移的发生条件
警戒 (Guard)
判定是否状态转移发生的条件
架构设计
作业分析
前两次作业都是输入一系列的UmlElement然后对这些UmlElement 进行解析, 将其分成不同的类
第一次作业类图:
将UmlElement解析为Class, Interface, Attribute, Operation, Parameter, Association, Generalization, InterfaceRealization.
第二次作业顺序图和状态图
将UmlElement解析为 StateMachine, Region, State Transition, Event, Interaction, LifeLine, Message
由于这些元素之间都是有关联的, 所以我们需要知道每一个UmlElement的parent是什么,
- 对于类图
UmlElement | parent |
---|---|
class | - |
interface | - |
attribute | class |
operation | class / interface |
parameter | operation |
类图还有三类特殊的UmlElement
表示类图元素的关系
-
association
end1 和 end2分别表示关联的两个端点
-
generalization
继承有source 和 target
-
interface realization
接口的实现, 同样有source和target
如此可以用图的思想建立类图
主要有两类结点:class 和 interface
class类包含的属性有attribute 和 operation
interface包含的属性有operation
operation又有自己的parameter
节点之间的关系可以表示成有向图的形式:
association就是双向的有向图, 相当于无向图
而generalization和interface realization 就是正真的有向图, 表示的是从source到target的一条边
- 对于顺序图
顺序图需要解析的元素只有lifeline, interaction, 和message
UmlElement | parent |
---|---|
interaction | |
lifeline | interaction |
message | interaction |
我们只需要建立一个特殊的interaction包含lifeline和message就能表示顺序图中的元素
- 对于状态图
状态图需要解析的元素有statemachine, region, state, transition, event
UmlElement | parent |
---|---|
statemachine | |
region | statemachine |
state | region |
transition | region |
event | transition |
statemachine之下的唯一儿子就是region
所以我选择用region存所有的状态而不是用statemachine
中间利用了一个statemachine的hashmap将statemachine和region之间的关系记录下来
这个特殊的region中包含了state和transition
transition又包含了自己的event记录
总体上就是将类图表示成一个图, 将顺序图和状态图分别表示成另外的两个图, 通过利用数据建立图的概念来对图中的元素和属性进行查询。
四个单元中架构设计及OO方法理解的演进
第一单元
第一单元涉及到了表达式的求导, 主要用到的架构就是对表达式进行正则表达式解析,之后将表达式分成表达式, 项, 因子等, 利用递归的思想对表达式进行求导。
第二单元
第二单元的电梯, 主要运用了多线程的概念, 架构主要是对调度器的架构。 主要运用了两层调度器, 第一层调度器对所有对电梯发出请求的人进行分类调度, 将所有的人放入第一层缓存区, 第二次调度是将缓存区的人放入不同的电梯序列, 然后通过电梯线程对不同的人进行分别的运载。
第三单元
第三单元几乎不涉及到架构, 因为都是按照给好的规格直接写函数, 谈不上架构
第四单元
架构如上所述
四个单元中测试理解与实践的演进
第一单元测试
第一单元测试应该是四次作业中测试做的最好的。 应为是正真能够测试的单元, 在第三次作业中首先对表达式序列进行了正则表达式匹配, 不对直接Wrong Fromat, 之后由于强大的python库, 我们可以对表达式进行求导, 并且利用python库进行表达式求导, 并且对比结果输出, 只要结果输出不对直接WA。
此外做测试还可以直接进行表达式计算, 对表达式带入值进行计算, 然后比较带入值后的表达式的结果。
此外测试还可以通过构造特殊样例进行验证, 随机化测试可以覆盖大量的点, 但是对于其中一些特殊的点, 可能覆盖到的几率非常的低,所以我们需要进行手动构造样例来进行验证。这样几乎可以将所有的情况涵盖, 只要构造的样例足够强的话。
第二单元测试
第二单元的测试依旧沿用第一单元的思路, 主要利用自动化测试让程序自己跑,这个一般能够测试出很多的bug, 当自动化测试已经不能很好的测bug的时候就需要手动构造样例。
实际的时候就遇到了在自动测试下长时间都没有出锅, 但是被构造的两行样例hack。
第二单元测试的最大困难就是多线程的不确定性。
主要表现在测试死锁的时候, 很多时候虽然你能将对方的bug找出来, 但是多线程具有不确定性, 所以你并不能保证百分百复现, 这样的bug不能用于hack, 但是你又能确定对方存在死锁的风险。在这个方面完全没有办法解决。
第三单元测试
第三单元就是按照规格写函数, 在前两次作业中有一点点的性能要求, 第一次作业更多同学没有使用并查集, 在查询销量上非常低, 从而被卡TLE。 第二次作业利用极端数据hack到人, 同样是TLE了。 第三单元作业由于实现难度确实比较低, 测试的时候主要利用了c++进行自动化测试,此外还有手动构造数据进行hack。
在测试的过程中也走了一些弯路, 一开始利用jmlUnitNG, openjml等做测试, 测试效果不是很好, 而且时不时就会跳出错误和警告, 测试体验不是很好。 后来用junit测试体验好了一些, 但感觉效率仍旧不是很高。 最后直接该用c++测试, 感受到了c++的强大。
第四单元测试
第四单元没有做完善的测试, 一方面是数据难以生成, uml图只能通过手动绘制, 所以测试销量低下, 基本就是过了样例就能过。 在课下手动构造了几组样例测试自己的程序, 而且由于第四单元没有互测,测试也做的很少。
课程收获
- 首先在oo实验的过程中java语言的理解有着质的提升,虽然上学期上过半学期的java课, 但由于实践太少, java的很多概念不是很熟悉, 对于正则表达式等知识点更是一窍不通。
- oo课程主要学到的还是面向对象的相关知识, 了解了工厂模式, 封装, 继承,多态等内容。 此外对java中类, 接口等概念有了深入的了解, 对深拷贝, 浅拷贝等知识有了一定的了解。 对java中的异常类及异常处理有了更深入的了解。
- oo课程主要给我带来的还是思想上的改变, 表达式求导的第一次作业我仍旧带着面向过程的思想来完成作业。而越到后面, 思维逐渐面向对象, 在电梯的调度上尽可能少的使用面向过程的方法, 而使用面向对象的方法。但是仍旧存在一个问题就是如何在尽可能面向过程的方向上提升性能, 所以在电梯单元很多与性能相关的地方仍旧使用了面向过程的实现。在未来这是一个可以努力的方向。
改进建议
- 建议合并jml和uml, 这两个单元知识都很概念化, 在实现上并没有达到前两个单元的难度, 单独开两个单元的意义不是很大。 而且后两个单元和前两个单元的落差确实很大。
- 互测的限制比较严格, 互测很多时候不能将bug找出来, 建议将互测数据范围改成和强测一样。
- 指导书说明不是很清楚, 特别是第四单元最后一次作业, 理解指导书花费了大量的时间。建议完善指导书的表述,加强语言的严谨性,清楚的传达内容。