BUAA OO Unit4 UML 模型的验证与测试
StarUML 使用
统一建模语言(UML)是一种提供针对性、分离的结构与行为描述手段而且可以将描述元素整合起来的语言。
UML 可以通过多种描述视角提供语法明确、语义清晰的可视化图示语言,相对成熟。在复杂大型项目中可使用 UML 建立可视化模型来方便开发人员和用户之间的交流及系统维护。
本单元作业要求我们实现图书馆系统设计的类图、状态图和顺序图。以下仅简单描述我在使用 StarUML 完成作业时遇到的问题:
- 作为初学者使用 StarUML 较为常见的问题:删除某个元素时
delete
表示仅在 diagram 中不显示,完全删除需要delete from model
。 - 绘制状态图时,在某个 transition 中添加 trigger,需要右击 transition 后 add,并修改 trigger 的 name。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AwKlqZZi-1687090255949)(C:\Users\Michelle\AppData\Roaming\Typora\typora-user-images\image-20230617194815605.png)]
- 绘制顺序图时,需要在 Lifeline 相应的 role 里修改 type 属性 。
需求分析
实现一个图书馆模拟系统
hw13:
- 三类借阅和预定规则不同的书籍
- 图书馆内有五类 staff:借还管理员(负责B类书的借还和书籍损坏丢失的惩罚)、自助机器(查询余书,C类书籍的借还)、预定管理员(负责登记预定和发放预定的书籍)、整理管理员(每过三日整理管理员手中的书并送给预定管理员需要的书)、后勤处(修复书籍)。
hw14迭代:
- 校际借阅(确认需要校际借阅的书籍,处理校际借阅中书籍的运输、接收和发放工作)
- 书籍加购(确认购书清单)
hw15迭代:
- 图书借阅期限,逾期归还会被惩罚
正向建模与开发
上述进行了一个总体的需求分析,然而项目需求中还有许多繁琐、细节性的逻辑结构需要梳理,需要引入基于UML的正向建模。正向建模要求我们进行透彻的需求分析并通过UML可视化模型呈现,学习模型化设计思维方式,有助于我们理清需求、设计、实现层次的结构和行为:通过类图按数据和行为提取抽象形成抽象层次结构,通过顺序图确定哪些对象参与交互及对象需提供的操作从而识别类之间的关联关系,通过状态图进一步完善类的行为,在这个过程中建立了关联层次,实现层次设计。以下是本单元中我基于UML的正向建模流程:
架构设计
以下为我的分层设计模型:
- 按数据和行为对题目提取抽象,确定类的个数和职责。bookshelf、student、book为容器类,6个staff、library、library network是控制策略类。
- 按数据管理或从属层次建立关联结构,按照行为实施层次建立分层设计。library network 管理了 library池子,library 管理了6类staff、bookshelf 和 students 池子。
- 按照对象的交互和状态的转移设计设计类的行为。比如说某个学生发出借书请求,首先在 library network 查找相应 library 并调用其 handleRequest 方法,self-service machine 查询图书馆书架上是否有余书,若有,borrowing and returning librarian 或 self-service machine 判断其是否能借阅,若能借阅,则书成功被该同学借走。
- 通过对比最终的代码设计和UML模型设计之间的追踪关系,实现程序前的设计和程序还是比较契合的,当然程序实现后还是需要对UML模型进行一些改动。
总结
在本单元设计中我深刻感受到了正向建模的重要性,本单元的需求是细致繁复的,考验逻辑和抽象能力,比如说有些行为存在相似性,如何抽象设计降低代码复杂度是比较重要的一个问题。在最开始设计的时候,我只是在纸上画了一个粗略的草图,没有完全理清关系,导致类的职责不明确,增加了耦合性。另外在本单元设计中我没有抽象出一个功能输出类是比较可惜的,降低了代码的美观性。
BUAA OO 学期课程总结
当开始写这个文档的时候,我真正意识到当我完成它后 OO 就结课啦,内心有些不舍和茫然。非常感谢 OO 课程为我打下了面向对象的基石,接下来的路需要自己去探索啦 ~
unit1——递归下降
架构设计思维
思维上从面向过程进入面向对象阶段。对象是指把逻辑聚合的数据和函数形成物理聚合,这方便了我们按照数据的处理流程、状态变化和处理层次来进行处理。第一单元是利用递归下降算法处理表达式,形式化语言描述让表达式结构一目了然。抽象层次结构利用继承和接口,通过对一组类共性行为的抽取结果使行为设计和行为实现相分离,减少代码冗余性。在第一单元设计中,表达式由项组成,项由因子组成,因子包括常量因子、变量因子等,可建立抽象层次,都实现 factor interface,简化引用并抽象出所有实现共性的接口(向下重用和扩展)。
测试思维
测试主要是通过评测机实现,python库中有 sympy ,正确性比对比较方便。另外按照形式化语言表述,对数据类型进行划分,形成输入划分层次树,做到覆盖性测试;观察数据范围,测试边界情形,逐步增大数据大小,进行压力测试,从而优化时间性能以及找出bug。
unit2——多线程设计
架构设计思维
线程、共享、交互形成了面向并发和协同抽象的层次结构,通过 synchronized 关键字、读写锁等线程互斥方式,保证线程安全,通过 wait、notify、join 等线程协作方式保证并发行为的协同和效率。
Pipeline 模式也称为流水线模式。将一个任务处理分解为若干个处理阶段,其中每个处理阶段的输出作为下一个处理阶段的输入,并且各个处理阶段都有相应的工作者线程去执行相应的计算。基于 Pipeline 模式,在多线程电梯实时系统中,线程有输入装置、调度器、电梯三类。输入线程和调度器线程共享请求队列。调度器线程和电梯线程共享电梯内等待队列。
如何建立稳定、灵活、健壮的设计呢?一是提取数据和行为的共性,分析类之间的关系,形成层次结构,将不同职责的对象划分到不同的层中实现。二是尽量遵循SOLID原则,提高程序的可维护性,比如说单一职责原则,要求每个模块只(完整)负责自己的事情,而不是万能的,这可以降低类内的复杂度和类之间的耦合。三是高内聚低耦合的设计,内聚是从功能角度来度量模块内的联系;耦合是软件结构中各模块之间相互连接的一种度量;通过简化接口设计,只对外暴露最小限度的接口;隐藏实现细节等方式降低软件复杂度。
测试思维
多线程的程序bug复现具有偶然性,应设置不同的数据场景测试,例如电梯系统中随机数据、高并发请求场景(压力测试)、所有请求要求同样的出发楼层或目的楼层等。还可以设置debug类,输出一些辅助信息观察错误原因。
unit3——规格化设计
架构设计思维
规格化设计分为数据规格和方法规格,数据规格为类所管理的数据内容及其有效性(invariant,constraint),方法规格为类所提供的操作,要规约前置条件require、后置条件和副作用,另外注意子类与父类的规格关系。规格与实现分离是指将规格说明与具体实现分开,以实现更好的灵活性、可维护性和可扩展性。比如说在unit3中规格定义了 people[]
,在实现时为了获取方便我们使用 HashMap
进行存储。虽然规格对数据和方法进行了描述,但只对模块接口进行规约,没有限制模块内部细节设计,所以数据容器的选择、中间数据的存储和检索、规格的层次化以及算法的选择依然是需要在实现中思考的问题。
测试思维
OK测试方法可以检验代码实现与规格的一致性,通过逐条比对规格与代码,可以发现代码中的错误,确保代码实现了规格中定义的所有功能。功能测试依据需求设计构造测试用例,观察输出是否符合预期,要求覆盖所有功能场景。单元测试是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作,确保每个功能模块都能够独立地正常运行,在测试 OKTest 编写是否正确时就采用了这种方式。另外就是评测机设计,主要就是增加随机数据大小进行压力测试。
unit4——模型化设计
架构设计思维
模型化设计指先使用系统化的模型语言来表示设计结果,进而开展设计与实现,通过类图按数据和类提取抽象形成抽象层次,通过顺序图完善类的职责和关联关系,通过状态图完善类的行为规格设计,进行正向建模。在使用starUML画类图的时候我个人感觉添加注释不太方便,当然也是我还是不太熟悉UML语言,导致有些东西我可能需要自然语言表述。
测试思维
通过正向建模可以理清数据状态变化等信息,从而划分数据场景,进行构造测试。
课程收获
我的架构设计能力得到了提升,在设计时会下意识增强类的封装性、高内聚低耦合等方式来降低软件复杂度;我的编程能力得到了进一步提升,从百行代码向千行代码的尝试;我的测试能力得到了进一步加强,不再仅仅局限于手动构造数据或者只是构造随机数据评测机,而是根据数据场景实现有针对性的、覆盖性以及更多形式的测试。在oo课程中我第一次接触较大型的程序设计,让我不断认识到架构设计的重要性,学会如何实现可读性和可维护性较高、复杂度较低的工程代码,相信以后面对大型项目能更加游刃有余。