第四单元
正向建模与开发
概念
正向建模与开发是一种自上而下的开发方式,从需求分析开始,建立模型,逐步细化,最终再根据模型实现对应的代码。
学以致用
本单元以一个图书馆管理系统为背景,锻炼对程序架构的设计和抽象能力,以及UML建模能力(包括类图、状态图和顺序图)。通过理论课对UML语言及模型的解析,建立对UML的认知的同时,学习其中使用的三个模型图,掌握基本模块概念和模块关系。类图作为静态图反应整体代码架构;状态图可用来描述关键对象(BOOK)的状态以及状态转换;顺序图通过展示对象间的消息传递体现系统的运作流程。
在充分理解整个图书馆管理系统的架构和运行流程后,开始初步的构思,再将想法落于实践,展示在UML类图上。
类图
系统中必需的模块与服务对象,思考其负责的功能
主要是场所:书架,借书处,预约处,漂流处;服务对象:学生。
根据已有的功能划分决定方法种类
各模块之间的关系
需着重注意的是管理借书处,书架,预约处等场所和服务对象(学生)的控制类(Library),同时其也管理后续的漂流处。
需要判断储存信息的方式,是直接使用容器还是使用类进行封装
在每次的作业迭代当中,对于出现新的需求,考虑到当前系统需管理信息的有限性,故仅在原有的架构基础上添加新的容器
状态图
- 通过书籍的位置反应书籍的状态
- 依据类图选择引起状态变化的事件(方法)
- 在书籍进行位置变换时,同时保证相关信息的传递
顺序图
- 运作流程也可以看做书的管理与传递流程,第三次作业中仅关注预约书籍的流动
- 一个对象可看做一个Lifeline,整个顺序图只要包含书籍流动过程当中涉及的相关对象即可
- 依据类图选择进行信息传递的途径(方法)
谈谈感触
- 通过建立模型再编写代码的方式,保证编写代码的整个过程逻辑清晰,熟悉代码的结构。避免中途由于设计问题导致的推倒重来,提高代码质量。
- 避免未充分理解需求就编写代码的情况,降低开发风险。
- 完成本单元作业的过程中,模型建立与代码编写的时间占比可以说是对半分,但整个代码编写的过程要相较于前三个单元要顺畅很多,不会出现思路紊乱,反复修改的情况。
架构设计
本单元通过设置控制类(Library),管理四个场所(书架,借书处,预约处,漂流处)和一个服务对象(学生)
书架、借书处、预约处、漂流处、学生均需记录储存各类书籍对应的余本数,预约处需要记录预约人及其预约的书籍,捐赠书流经的借书处、学生、漂流处需要记录捐赠书的捐赠书和借阅成功次数
控制类接受请求,调用场所和服务对象提供的方法判断是否接受请求,并根据请求完成书籍的移动
代码 - UML模型间的追踪关系
- 元素对应:UML模型中的每个元素(如类、接口、方法等)都应在代码中有对应的实现,同时,类图中每个方法需与程序中类的方法的名字、可见性、返回值类型一致,每个类的属性需与程序中类的属性的名字、可见性、类型一致。
- 关系一致:类图中的继承、实现、关联关系应和代码实现中的关系保持一致
- 行为一致:状态图中的行为(如方法调用、状态转移等)应在代码中得到一致的实现(本单元中为状态图中迁移的Trigger必须和程序中有@Trigger注解的方法一一对应)
- 消息一致:顺序图中的传递消息(同步,异步等)的方法应在代码中得到一致的实现(本单元需要保证任何一个 message 的名称应与 source 所对应类或 target 所对应类的某个方法相同;任何一个 message 的 source 和 target 所对应的两个类如果不同,两个类之间应有关联关系)
在实际的程序开发过程当中,由于UML模型设计之初未考虑充足,可能会出现实现代码过程当中,遇到需要更改UML模型的情况。
整体回顾
设计思维
经过每一个单元的学习,代码的设计思维都有一定的提升,对代码架构与设计思路有新的认识。
- 容器选择:根据需求选择合适的容器,例如对查询操作需求高的信息,需使用Hash类容器,对有一定顺序的信息,可使用队列或者大(小)顶堆等容器。
- 使用类进行封装:除了出现具体的,可操作的实体或者依据职责单一原则时,需要新建类外,当有一组相关的数据(属性或字段)和与这些数据交互的方法(行为)时,需要将其封装在一个方法内,便于理解与统一调用
- 使用继承实现类的层次关系,以及接口进行定义规范:前者可实现代码重用,多态性,后者可实现解耦与多重继承。
第一单元
层次化设计(递归下降方法的应用):完成对表达式的充分解析并能较好满足新元素融入后的调整迭代
第二单元
设计模式:根据需求选择合适的设计模式,可简化需求实现,只需关注具体的细节问题
调度策略(情况复杂):调度策略有很多,ALS策略,LOOK策略,影子策略,量子电梯等,不同策略对应着不同的设计思路,其需要考虑的因素是不同的
运行策略:使用主请求切换策略与捎带策略指导电梯运行
第三单元
JML规格与实现分离:规格定义方法或系统需要满足的要求,包含限制规则,前置条件,后置条件,副作用范围限定等,但不需要关注实现的方法与过程,编写代码时依据规格实现
维护策略:为避免反复查询操作导致的时间浪费,通过对需要查询的变量进行维护,查询时直接获得的方式节约时间
算法设计与应用算法可看做一种设计思路,能更快地解决问题,而某些算法需要特定的数据结构,需要依据算法构造相应的数据结构
第四单元
正向建模的开发思路:这是是一种自上而下的开发方式,从需求分析开始,建立模型,逐步细化,最终再根据模型实现对应的代码。
测试思维
本课程的学习并使用了多种数据构造策略与测试策略,针对测试程序选择合适数据构造策略以及测试策略
数据构造策略
随机生成:根据数据格式与范围限制,通过随机生成数来构造测试数据
依托一定的构造策略:
第一单元,依托形式化表达构造特定数据,保证(每个对象都要出现,每种组合规则都要出现,每种符号模式都要出现,每种系数模式都要出现)
第三单元,为每一种指令构造其生成函数,通过维护可选数据的容器,在生成指令时,从容器中随机选择数据
测试策略
对拍测试:将被测试代码的输出结果与标准程序的输出结果对拍(检测数据是否相同),判断是否正确
同步模拟:测试程序依据被测试程序的输出,构建相似的数据结构储存输出记录,通过已有的记录判断新的输出是否符合评测要求(eg
:第二单元的电梯作业评测)
junit
测试:通过JUnit
提供一套断言方法(如assertTrue()
, assertFalse()
, assertNull()
, assertNotNull()
等),故能够通过断言方法验证代码的行为是否符合预期的规格。如果实际输出与预期不符,测试将失败,并给出相应的错误信息,帮助开发者快速定位问题
交互式评测:第四单元使用交互式评测,部分请求通过已有的测试数据点给出,其他请求则根据被测试程序的输出动态生成,前者是固定存在的请求,由于不同策略对前者的处理的差异,为保证后者的有效性,需要依据输出动态生成,保证测试数据的有效性与针对性
课程收获
除了收获上述设计思维与测试思维之外,面向对象编写代码的能力得到锻炼。
代码bug
只要程序,无论如何都有可能有bug,所以在每次强测前,都会担心自己的程序的哪个小角落会有bug。在编写代码时的状态严重影响代码质量,特别是在半夜赶代码时,第二天会发现有数不清的bug,甚至整个思路都是错误的,由此对第四单元的使用UML正向建模有着强烈认同感,在没有想清楚架构并保证实现的正确性前,盲目编写代码是一件多么错误的事情。
debug与减少bug:
debug
的前提是程序有bug,但不得不承认的是,每次写完代码,程序总是有bug(诶)。我常用的是瞪眼法,面向评测debug的方法,但往往会出现瞪眼法只适用于实现与想法冲突的情况,而面向评测debug由于测试数据的局限性,不能完全验证程序的正确性,所以bug真的太烦啦!只能尽可能保证实现思路与方法的正确性以及代码编写时不写或少写bug(困难哇)
评测机:
首先要感谢陈旭传同学的评测机,从第一单元一直陪伴我们到第四单元,帮我找出很多bug;自己则参与到第一单元的评测机设计与扩展,并在第三单元独自完成评测机搭建,上传到github
提供给小范围学生使用,进行迭代优化,开发感受跟完成单元作业相似,但在进行迭代优化时需要考虑的是用户(使用者)的需求 —— 及时性(尽快完成)与有效性(数据够强),仿佛自己将一个产品投入到一个小的市场当中,最终为得到用户的肯定感到雀跃与鼓舞。
博客指导:
感谢学长的博客,学长的博客给我带来很多建议与指导,帮助我更好完成课程作业!
一路走来:
在第一单元中,逐渐适应OO节奏,体验学以致用的充实感,在不断优化的过程,既担心错误的发生,又想要追求更好的性能;在第二单元中,与室友进行激烈讨论,验证并实现自己的策略,面对需求变化改进策略,不断体验重构与debug的快乐;第三单元感受UML将规格与实现分离,使用更加优秀的算法以及面对时间复杂度时的被迫维护;第四单元使用正向建模与开发,先设计再实现,通畅极啦。整个过程下来虽然会有煎熬与崩溃,但最终收获到知识与快乐!
享受写代码:
对java面向对象的编写代码有了深入的理解后,在IDEA编译器内使用java编写代码是一件很舒服的事情,感受着将自己的想法用代码实现并展现出来,看到自己的程序能处理各种复杂的输入信息,得到正确的结果,真的很有成就感!