当前位置:   article > 正文

BUAA-OO 第四单元作业:UML 建模 与 课程总结

BUAA-OO 第四单元作业:UML 建模 与 课程总结

北京航空航天大学—计算机学院—面向对象设计与构造—第四单元与课程总结


前言

OO 第四单元作业的主题是——UML 建模。本单元作业训练的主要目标是设计。课程组鼓励我们在程序中融入自己的想法,引领我们体验正向开发的流程。

最终 UML 类图

UML

最终 UML 状态图(图书状态)

状态图

UML 顺序图(预约流程)

顺序图


一、第十三次作业分析

代码设计分析

a. 设计

本单元作业训练的主要目标是设计,所以题目限制较为宽松。课程组也提供了非常好用的数据结构和相应的输入解析、输出功能,方便我们使用。按照训练要求,我们选择先利用 UML 构建架构草图,将需要设计的类和比较明确的关联关系画出,再进行代码编写,最后根据完成的代码补全相应的属性、方法和其他关联关系。
对于具体的设计思路,我们不难想到去为图书馆相关办事处设计类(本质上是单例模式),设计学生相关的类。Library类作为几个办事处的集合处理输入请求,并决定整理流程。

// Library

public void dealRequest(LocalDate date, LibraryRequest request) {
        // new 或 get 到 student
        LibraryRequest.Type type = request.getType();
        if (type == LibraryRequest.Type.QUERIED) {
            // searchService
        } else if (type == LibraryRequest.Type.BORROWED) {
            // lendService
        } else if (type == LibraryRequest.Type.ORDERED) {
            // orderService
        } else if (type == LibraryRequest.Type.RETURNED) {
            // returnService
        } else { // PICKED
            // pickService
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

b. 数据结构

在实现时,因为一个人可以对同一本书名的书预约多个副本,取书时取走最早送到预约处的,所以我们会发现需要有关student, libraryBookId, LocalDate, Integer 等多重元素作为索引。而单独为其开一个数据结构作为类没有面向对象的具体意义,所以可能存在HashMap中嵌套多重HashMap的情况。
这里我采取Map.Entry<Student, LibraryBookId>的二元组作为HashMap的索引(一种举例),这样可以有效降低HashMap的深度。

c. 整理

具体整理方面的实现细节因人而异,但也有共性

经过思考我们发现,图书馆开门是一切的根源~ 只要图书馆不开门就不需要对 bookshelf ,borrowAndReturnOffice, appointmentOffice 整理~

换句话说,方便起见,我们可以将 ao → bs, bs → ao 这两个涉及时间记录逾期时间整理的操作都安排在开门后进行(实际上,所有的整理工作都可以安排到开门进行,关门就准点下班 > _ <)

因为要在 bs → ao 时记录时间,我们考虑利用 HashMap<Map.Entry<Student, LibraryBookId>, TreeMap<LocalDate, Integer>> 来记录

其他的更多的是处理上的细节问题。需要注意的是:画 UML 类图时一定要关注 StarUML 右侧的代码区,画布上没有显示的内容可能隐藏在代码中,是我们没有删除干净的!还有就是图中的某个字段为空的情况,也要小心~


二、第十四次作业分析

代码设计分析

hw14 基于 hw13 之上新增图书漂流角相关要求,主要聚焦状态图的绘制。
在设计时,我们不难想到先在 StarUML 类图中创建 BookDriftCorner类,和其他办事处一样与Library类具有关联关系。实现时注意相关请求的细节就好,没有过多数据结构方面的变化。
对于续借相关操作,我们继承 hw13 图书馆类的全局处理功能,在此基础上新添续借服务
具体处理则考虑在学生拿到书籍后,也记录拿到的时间(或记录图书逾期的时间),方便续借和后续还书时是否逾期的处理。


三、第十五次作业分析

代码设计分析

hw15 基于之前的作业上新增信用分系统相关机制,并新增顺序图的绘制。
同样,设计时,我们不难想到为Student类新增credit属性和相关增、删、查等操作。并在各个办事处的服务中进行信用分相关操作。
本次作业至少有两个细节需要我们注意:

  • 因为信用分的上限为20,但是没有下限,我们不难想到采取如下方式实现相关操作
    public void addCredit(int num) {
        credit = Math.min(credit + num, 20);
    }
    
    public void delCredit(int num) {
        credit -= num;
    }
    
    public int queryCredit() {
        return credit;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    但请注意,在图书馆相关服务中(本次作业具体落实到整理时的操作),如果既需要加分又需要减分,我们需要先减分再加分,否则带来错误的计算!
  • 本次作业不再允许一个学生可以预约多本同一书号的书,所以我们需要增加相关判断,同时也可以修改简化之前的数据结构,使我们的逻辑更加清晰(小规模重构~)

课程总结

一、正向建模与开发

利用 UML 进行正向建模设计 Java 应用程序的流程通常包括:

1. 确定需求

最开始我们需要明确应用程序的需求,确定需要实现的功能。
具体到本单元作业:每次作业我们需要认真阅读指导书,对于自然语言描述产生的二义性问题需要及时与同学讨论或询问助教,并随时关注讨论区,确定需要实现的功能。

2. 绘制类图

类图是描述系统中类、接口、关系和属性的 UML 图表。根据系统的概念设计创建类图,标识系统中的各个类、它们的属性和方法,并定义它们之间的关系,如继承、关联等。
具体到本单元作业:每次作业我们考虑先在类图上增加想要新增的相关类和明显的关联关系。我们不需要在画类图时就确定实现时每一个属性名、方法名,但需要确定大致的框架,方便我们设计并在开始就避开一些错误

3. 绘制状态图

状态图是一种 UML 图表,用于描述对象在其生命周期中的状态变化。根据类图创建状态图,展示对象在不同的操作或方法调用之间的状态切换流程。
具体到本单元作业:绘制出图书的状态图可以有效帮助我们直观建立程序运行逻辑,直观看出自己设计是否存在缺陷(如重叠的guard,死循环等)。状态图也方便我们向他人直观呈现我们程序的功能~

4. 绘制顺序图

顺序图是一种 UML 图表,用于描述对象之间的交互顺序。根据类图和系统的工作流程创建顺序图,展示系统中的各个活动及它们之间的关系和条件。
具体到本单元作业:绘制出预约到取书流程的顺序图可以有效帮助我们理清类与类消息传递逻辑,呈现运行逻辑~

5. 编写代码

完成 UML 建模后,便可以基于设计开始编写 Java 代码。使用 UML 图表作为参考,根据类图中的类和方法编写相应的 Java 类和方法,实现相应功能。
具体到本单元作业:类图、状态图、顺序图有效帮助我们建立程序架构,方便编程

6. 进行测试和调试

在编写完代码后,我们需要进行测试和调试。
具体到本单元作业:根据类图、状态图和顺序图中定义的交互流程编写测试用例,确保应用程序按照预期进行操作。根据 UML 图搭建评测机,辅助我们测试~

7. 部署和维护

在所有工作完成之后,将应用程序部署到适当的环境中,并对其进行维护和更新,以确保其正常运行和满足用户需求。
具体到本单元作业:每一次作业提交,下一次作业的迭代

二、代码设计和 UML 模型设计之间的追踪关系

在整个开发过程中,确保类图、状态图和顺序图的一致性至关重要:

  1. 维护系统完整性:如果三种图不一致,可能会导致系统行为的误解,从而引发潜在的设计缺陷和实现错误。

  2. 提高系统可维护性:一致的图表使系统更容易理解和维护。未来在系统扩展或修改时,我们可以依赖此进行准确的更新

  3. 确保功能和需求的正确实现:确保功能和需求的正确实现,辅助我们编写测试程序(本单元作业中,如果不一致还会导致评测错误~)

通过确保类图、状态图和顺序图的一致性,能够有效提高开发过程的透明度和可靠性,从而确保最终程序的质量和可维护性


三、架构设计思维的演进

第一单元

  1. 问题的抽象
    “面向对象”的世界观为:一切皆是对象。第一单元强调抽象的重要性,递归下降的思想贯彻三次作业。我们需要从具体问题抽象出若干对象,如本单元的表达式因子。从问题中抽象出一系列对象,我们可以建立更加层次化、模块化的结构,降低问题的复杂度。

  2. 模块化思维
    第一单元强调模块化的思维方式。通过学习,我更加注重将复杂的系统集合分解为各个独立的模块和组件,其分别负责各自逻辑处理,并通过定义清晰的接口和关系将它们聚合在顶层,实现更加复杂的功能。这种模块化思维显著提高了系统的可扩展性、可维护性和复用性,为我第一单元作业保驾护航。

  3. 面向接口编程
    第一单元强调面向接口编程的重要性。我学会了定义清晰接口,并在代码中使用接口进行交互,而不是直接依赖于具体的实现。这种面向接口编程的思维方式符合高内聚、低耦合的思想,并方便我们进行测试。

第二单元

  1. 设计模式的应用
    通过第二单元的学习,我理解并有效运用了多种常见的设计模式,如单例模式、生产者-消费者模式、状态模式等。在实际的架构设计中识别出模式应用场景,并有效融合应用它们,可以实现特定的功能,提高系统的灵活性和可维护性。

  2. 多线程的交互
    第二单元我们初次接触多线程。在多线程的场景下,对象之间的交互时机、交互结果都具有不确定性,我们需要结合实际场景对线程安全问题进行分析和解决——哪些对象是共享对象?哪些对象之间的交互存在着安全隐患?如何合理加锁(既能防止死锁,又能保证效率)?

  3. 性能和可伸缩性考虑
    学习第二单元后,我开始更加关注系统的性能和可伸缩性设计。我更加注重数据结构和算法的选择,以及系统的并发性和扩展性等方面的考虑。通过选择更高效的数据结构和算法,并设计在大规模和高并发环境下仍能稳定运行的系统,我提升了系统的性能和可伸缩性。

第三单元

  1. 契约式编程
    第三单元作业基于 JML 展开,我们在"面向规格编程"的过程中感受到“契约式编程”的魅力——高可靠性、高复用性、便于测试。我们同样学习了一些设计原则,如单一职责原则、开闭原则等,助于我们设计更加健壮和灵活的系统。

  2. 算法设计
    第三单元作业还涉及一部分算法,旨在优化我们的程序性能。包括且不限于并查集的使用、广度优先遍历、动态维护、缓冲进制的引入等。这启示我们在程序基本功能实现的基础上优化我们的性能,带来更好的效果。

第四单元

  1. 正向建模与开发
    第四单元训练的主要目标是设计。经过本单元的训练后,我们养成在实现代码前先画画类图、状态图、顺序图的意识,明确了设计实现之间的逻辑关系,体会到 UML 建模的强大之处。

四、测试思维的演进

本学期代码开发作业中,自动化测试回归测试贯穿我的每一次程序测试,辅助我进行修改程序漏洞

第一单元

  • 单元测试与自动化测试
    第一单元我实践了单元测试。基于面向对象先导课程的学习,并通过进一步理解,我更加注重编写可靠、独立和可重复执行的单元测试。
    同样,我注重捏造边界数据点进行测试,并借助评测机进行自动化测试。评测机的数据生成部分需要关注其随机性和覆盖性,尽可能确保涵盖多种可能。

第二单元

  • 集成测试和系统测试
    第二单元我关注集成测试和系统测试。基于自动化测试,编写集成测试来测试多个组件之间的交互,并使用系统测试来验证整个系统的功能和性能。通过关注集成测试和系统测试,我们更能全面地保证系统的质量和稳定性。
    第二单元需要我们尽可能复现死锁操作(如果有)。在实现过程中,我们注意锁嵌套时的实现逻辑;在测试时,我们利用大量随机生成的数据进行轰炸测试。

第三单元

  • 测试驱动
    第三单元中,我学习了如何在编写代码之前先编写测试,具体是基于 JML 规格进行编写(甚至不需要考虑性能问题)。该实践能够帮助我更好地发现问题,提高代码质量和可维护性。

第四单元

  • 自动化测试
    第四单元对于程序的测试综合集成前三单元的思想,搭建评测机进行自动化测试。需要注意数据覆盖情况和交互式正确性检测方面逻辑

总结

通过本学期面向对象设计与构造课程的学习,我的测试思维得到了提升:

  • 单元测试意识的加强:更加注重编写可靠、独立和可重复执行的单元测试,确保代码的正确性和稳定性
  • 集成测试和系统测试的考虑:关注组件之间的交互和整个系统的功能与性能,保证系统的全面质量和稳定性
  • 测试驱动开发的实践:在编写代码前先编写测试用例,通过逐步添加代码使测试通过,促进更好的设计决策和代码质量
  • 自动化测试:使用自动化测试工具和框架,提高测试效率
  • 回归测试:保证自己在代码新功能迭代过程中不向之前的功能引入 bug

五、课程收获

通过本学期面向对象设计与构造课程的学习,我的架构设计思维得到了提升,编程能力得到了锻炼

  • 面向对象的思维方式:从面向程序到面向对象的转变也增加了我思维上的维度。面向对象强调将问题划分为相互关联的对象,并通过对象之间的交互来解决问题,这使我能够更好地理解和设计复杂系统
  • 继承和多态:继承和多态机制让我感到复杂,惊叹其奇妙。其有效提高代码的可重用性、灵活性和扩展性
  • 从功能实现到抽象和模块化思维:更加注重将复杂系统分解为模块和组件,提高系统的可扩展性和可维护性
  • 面向接口编程:提高系统的可测试性
  • 应用设计模式:解决特定设计问题,提高系统的灵活性和可维护性
  • 性能和可伸缩性考虑:关注数据结构、算法选择及系统在高并发环境下的表现
  • 设计原则的应用:遵循高内聚、低耦合、可扩展和可维护性的设计原则
  • 理解和应用软件架构模式:根据需求和约束选择适合的架构模式

这种思维的演进使我能够设计出更高质量、可维护和可扩展的软件系统

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号