面向对象第四单元博客作业

仇志轩-22373135 学生 2024-06-16 21:06:02

本次作业要求撰写的内容全部用📝️标出。

课程要求

第四单元的任务是设计一个图书馆管理系统,主要目标是掌握对程序架构的设计和抽象能力,以及 UML 建模能力。

简单来说,图书馆每天白天开馆,夜晚闭馆(有时也会全天闭馆)。图书馆管理系统相应的任务是:在开馆期间接受各种用户请求,输出处理结果;在开馆和闭馆时整理图书,并输出图书的转运路径。

除此之外,第四单元与前三个单元的一处显著不同在于:我们需要先设计程序架构,然后使用 StarUML 绘制 UML 图,最后根据所设计的架构进行编程实现。

第四单元分为以下三次迭代:

  1. 第一次迭代需要实现借阅、还书、预约、取书、查询功能,以及开馆和闭馆时的整理功能。

    除此之外,还需要提交 UML 类图。

  2. 第二次迭代需要实现续借和图书漂流功能,并增加了图书借阅期限限制。

    除此之外,还需要提交 UML 类图和状态图。

  3. 第三次迭代需要实现用户信用分系统。

    除此之外,还需要提交 UML 类图、状态图、顺序图。

📝️架构设计

类图

以下是最终的类图:

类图

可以看到,只用了数量很少的类就完成了复杂的功能:

  • Library 类:顶层接口,处理开馆、闭馆、请求等各种命令。
    • 数据:图书馆各个位置的数据、所有预约请求、所有用户数据。
    • 方法:开馆、闭馆、查询、借书、预约、还书、取书、续借、捐赠等。
  • Student 类:管理单个用户。
    • 数据:学号、信用分、已借阅书籍。
    • 方法:借书、续借、还书、查询等。
  • Inventory 类:管理一系列书籍,是图书馆各个位置和用户“背包”的抽象。
    • 数据:一系列书籍。
    • 方法:添加书籍、删除书籍、查询书籍等。
  • Book 类:管理一本书籍。
    • 数据:ID、借阅信息等。
    • 方法:状态转移(借书、还书、预约、续借等)和状态查询。
    • BookState 类:书籍状态的枚举量。
  • LibraryClock 类:管理图书馆的时钟,被其他类用于检查操作合法性。
    • 数据:当前日期和状态。
    • 方法:状态转移和状态查询。
    • LibraryState 类:图书馆状态的枚举量。
  • Main 类:主类,负责输入输出。

状态图

以下是最终的状态图,描述书籍的状态:

状态图

以下是书籍的五种状态,恰好对应五个位置:

  • FREE:(正式)书籍位于书架,可自由使用。
  • U_FREE:(非正式)书籍位于漂流角,可自由使用。
  • ORDERED:书籍位于预约处,等待用户取书。
  • BORROWED:书籍位于用户手中,正在被借阅。
  • COOLDOWN:书籍位于借还处,等待被移回书架或漂流角。

ORDEREDBORROWED 下方还注有一些属性名称,它们是仅在对应状态下可用的附加数据。

值得一提的是,因为这五种状态“恰好对应五个位置”,所以许多人认为它们是多余的——完全可以通过图书所处的位置来判断图书状态。

而我呢?我在第一次迭代时就为 Book 类设计了有限状态机,并绘制了简单的状态图。之所以这样做,并不是因为猜测或得知了第二次迭代要求画状态图,而是为了实现代码的自纠错——在图书状态出错时自动终止程序。后来我在网上查找资料时,得知这种设计方法被称为快速失败

状态图初稿

私以为状态图是这三类 UML 图中最有用的一类。有限状态机的思想在面向对象设计中也能发挥重要作用。

时序图

以下是最终的时序图,描述预约的过程:

时序图

可以看到,虽然我在状态图中已经略去了部分细节,但完整的流程依然很复杂——尤其是分支结构的嵌套。

📝️模型设计与代码实现的关系

在第四单元中,我们尝试了正向建模与开发,也就是先建立模型,再根据模型编写代码。

在本单元的每次迭代中,我都试着先设计,再编码,然后根据代码反过来修正设计,在反复修改中完成一次迭代。这种做法既能通过设计建立一个合理的、易用的架构,也能通过实现来补全架构中模糊不定的细节。相比之下,有些同学选择跳过设计直接编码——这样就很容易导致架构混乱,难以维护。

可惜的是,由于设计不足,编码过早,我在第三次迭代时还是出现了设计问题。在第三次迭代中,需要在已借阅书籍到期的当天对用户进行信用分的扣除。这就引出了两种选择:

  1. 存储所有正在被借阅的书籍,每天从中筛选出刚刚过期的书籍,对其借阅者进行扣分。
  2. 每天向所有用户发起查询请求,查询他们手中刚刚过期的书籍,并对用户进行扣分。

方案一效率占优,且具有一定的扩展性(如果题目要求新增查询历史记录的功能的话,稍加修改即可实现)。而方案二代码量占优,无需对架构有大的修改。我没有充分考虑就选择了方案一,为此新增了 RecordRecordTable 两个类共计九十余行代码。后来我在重构期间,经过考虑认为这两个类没什么用,于是决定删掉这两个类,改用方案二。

除此之外,我的设计不足的问题还体现在下层接口上。在编码前,我虽然对需要新增的功能设计了顶层接口和数据结构,但并没有为其他类设计接口。这就导致我在编码时为了调用方便而乱开接口,这些接口功能混乱且用途单一,不少接口都只有 1 处引用。没办法,能花在 OO 上的时间还是太有限了。

最后我想谈一下建模阶段 UML 图的绘制。

虽然 UML 的功能十分丰富,但我们需要的只是其中的一小部分。这是因为我们使用 UML 仅仅是为了设计,而不是为了取代(所有或者部分)Java 代码。所以,UML 图不应画的太复杂,更不应 1:1 复刻代码,只要表达出代码的基本架构和运转逻辑,这就够了。

📝️课程收获

通过本单元的学习,我的建模能力有了很大的提升。现在我不仅可以在编码前进行设计,也可以用 UML 图准确规范地表达设计成果。这项技能可以加快编码速度、提升代码质量,对我们有很大帮助。

总的来说,《面向对象设计与构造》是一门富有挑战性的课程。它向我们展示了一种全新的思维模式——面向对象的思维,并通过四个单元的迭代开发,将这种思维付诸实践,提升了我们的面向对象的理解,加深了我们对面向对象的认识,锻炼了我们使用面向对象解决问题的能力。

这门课程锻炼了我的架构设计思维:从第一单元的层次化设计,到第二单元的多线程设计,我学会了使用面向对象思维设计复杂的架构。从第三单元的规格建模,到第四单元的 UML 建模,我学会了使用建模语言和建模工具对设计进行规范化。

这门课程也锻炼了我的测试思维:功能代码与测试代码总是相辅相成,共同进步。虽然测试的核心无外乎数据生成和评测这两部分,但构造高质量的数据总是难点。为此,我们需要反复阅读题目要求,确保数据生成器生成的数据既能满足题目要求(不超出数据范围),又能覆盖每一种情况。

面向对象课程终于结束了,一个学期的努力换来了一箩筐的知识,希望 OO 课能成为我们生命中一笔宝贵的财富。

...全文
132 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

301

社区成员

发帖
与我相关
我的任务
社区描述
2023年北航面向对象设计与构造
学习 高校
社区管理员
  • YannaZhang
  • CajZella
  • C_ecelia
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧