308
社区成员
发帖
与我相关
我的任务
分享第四单元的三次作业都围绕图书馆系统迭代。
一阶类图首先要回答:
Book、BookCopy、User 和 OrderRecord;它迫使我在写代码前确定职责边界。若没有这一步,很容易把输入输出、规则判断、状态修改和图书移动全部堆进一个管理类中。
实现会暴露一阶设计中没有考虑清楚的细节。二阶类图的任务不是照抄代码,而是吸收这些实现反馈,形成能够解释最终系统的“现状模型”。
一阶和二阶类图真正的变化集中在职责细化上:
applyCloseCreditPenalties 被拆成借阅逾期、阅读未还和预约失效三类处理,分别对应不同事件和数据来源;BorrowLimitChecker 增加 canPick,避免将取书和普通借书错误地套用同一组信用限制;User.hasBorrowedCopy、BookCopy.isOverduePenalized 补足所有权检查和防止重复扣分的语义;RequestResult 将 acceptWithInfo 调整为 accept 重载,使结果对象的接口更统一。我认为,两阶类图的价值在于差异是否能被解释。一阶图给出设计假设,代码检验假设,二阶图记录经过检验后的结论。若二阶图与一阶图完全相同,却无法对应代码,那只是没有把实现中的认识带回模型。
最终代码可以分为四层职责:
flowchart LR
Main --> LibrarySystem
LibrarySystem --> RequestService
RequestService --> BorrowLimitChecker
RequestService --> LibraryManager
LibraryManager --> InventoryCatalog
LibraryManager --> ArrangeService
LibraryManager --> User
LibraryManager --> Locations["LibraryLocation 地点体系"]
InventoryCatalog --> Book
Book --> BookCopy
User --> OrderRecord
User --> BookCopy
LibrarySystem 是系统边界,负责识别官方包命令并输出结果,不保存业务状态;RequestService 组织借书、预约、阅读、续订等用例,BorrowLimitChecker 集中判断数量和信用限制;LibraryManager 协调多个对象的一致修改,ArrangeService 专门处理开馆前和闭馆后的时间驱动流程;Book、BookCopy、User、OrderRecord 保存领域状态,地点类负责保存各处实际持有的副本。其中 Book 与 BookCopy 的分离最重要:评分和借阅期限属于 ISBN 对应的书目规则,位置、预约对象、到期日和移动轨迹属于具体副本。这样新增精品书架和借阅期限时,不需要推翻原有对象结构。
LibraryManager 虽然承担较多协调职责,但规则判断、整理算法和实体状态已经分别交给其他类。它更接近事务协调者,而不是包办所有逻辑的“上帝类”。
| 需求 | UML 设计 | 实现方法 |
|---|---|---|
| 借书、预约、取书 | RequestService、BorrowLimitChecker、OrderRecord | borrowBook、orderBook、pickBook |
| 阅读与归还 | ReadingRoom、User.readingCopy | readBook、restoreBook |
| 评分与精品书架 | Book、GradingOffice、TreasuredBookshelf | grade、isTreasured、开馆前重分类 |
| 借阅期限与续订 | Book.getBorrowPeriodDays、BookCopy.dueDate | startBorrow、renewBook |
| 信用分 | User.creditScore、BorrowLimitChecker、LibraryManager | 分数边界、权限检查和三类扣分事件 |
| 图书整理 | ArrangeService、ArrangePhase、地点类 | arrangeBeforeOpen、arrangeAfterClose |
静态结构上,二阶类图的 23 个设计单元与源码一一对应;AbstractBookLocation 实现 LibraryLocation,五个具体地点类继承它。类图中的主要字段、方法和关系都能找到代码依据。
动态行为也可以追踪。状态图以 BookCopy 为核心,共有 18 条迁移,其中 17 条业务迁移对应源码中的 @Trigger;续订只修改 dueDate,不改变位置,因此没有被错误画成状态迁移。顺序图中的五条生命线和 14 条消息,则对应预约、限制检查、订单创建及取书的真实协作方法。
模型与代码仍存在合理的抽象差异。例如代码包含借阅天数、信用分增减值等私有常量,类图没有全部展开;部分关联通过属性类型隐式表达,也没有全部画成连线。我认为追踪关系不是逐行复制,而是每条需求都能找到负责的类、方法和状态变化,同时每个核心设计元素都有真实业务依据。
在本单元中,大模型在以下几个方面帮助进行架构设计。
三次作业的规则逐步叠加,其中包含很多时间边界、失败条件和状态约束。大模型可以帮助我将需求整理为实体、事件和不变式。
对于同一个需求,大模型可以快速给出多种职责划分方案,并提醒我检查高耦合和职责集中问题。例如,借阅限制可以直接写在 RequestService 中,也可以抽离到 BorrowLimitChecker。通过对比后,我选择后者,因为信用分规则在第三次作业中变化很大,独立策略类更容易扩展。大模型提供备选和利弊,最终取舍仍需要结合项目的变化方向。
两阶类图、状态图、顺序图和代码之间存在大量可机械核对的内容。大模型能帮助比较类名、字段、方法、继承关系、@Trigger 和顺序图消息,并指出缺失或多余的元素。这对二阶类图尤其有用,因为人工检查很容忽略可见性、返回类型或参数顺序等细节。
在四个单元中,第一单元的架构设计感觉是一种从自己的架构向着公众号架构演变的过程,一开始我没有采用结构和算术分离的架构,在第二次作业中架构太复杂了,才向着公众号给出的单项式-多项式的那种结构进行。第二单元的作业,先看了建议的三层架构,然后才开始完成作业。从前两次作业来看,课程组给出的推荐架构都非常好,以致于整个架构或多或少都是在课程组给的建议上修改的。尽管如此,在大架构下的一些小架构也需要自己的一些小巧思,我觉得这些自己思考的小架构方面对我的架构设计很有帮助。
第三单元的架构已经给出了,因为是照着JML写代码,感觉并没有自己设计一些东西。第四单元我认为对我架构设计思维的帮助很大。不像前一、二单元过多的算法上的占比,第四单元难的是系统的复杂度,在这样庞大的复杂度下,就需要好的架构来理清思路,在大模型的帮助下,我在第四单元建立起了一个还算满意的架构。
我觉得四单元的测试的思维都非常不一样。我在前两个单元都搭建了评测机,前两个单元的测试主要是依靠极端数据的生成,评测的有效性很大程度上取决于数据生成器能不能生成极端数据,或者是用大量的数据去碰运气找到极端数据。在第三单元中,测试被具象化为了Junit,Junit和前两个评测相反的是,要求白盒的测试,要照着代码写测试,并且由于测试的不是自己的代码,实际操作起来会发现各种各样的问题。第四单元我几乎没有使用评测机,系统的复杂程度让评测更加关注每一个方法实现的准确性,算法上的宽松也使得大规模的黑箱极端数据测试变得不那么重要了。
面向对象这门课确实很有意思,前两个单元给我留下很深刻的印象,对代码进行一遍遍的修改,造各种极端数据的评测机,发现题目中的一些小巧思,后两个单元理论居多,压力小的同时作业也稍显无聊,无论是照着JML写代码,还是在starUML里面点点点。尽管如此,后两个单元给了我思想上的启发,包括真正在大项目中如何管理代码,如何协作,如何测试。在大模型时代,设计和实现分离的特点更加明显,面向对象这门课程提供的思想,包括抽象、解耦,都是在回答如何更好地使用大模型完成更底层的代码实现,这是使用大模型不可或缺的部分。