309
社区成员
发帖
与我相关
我的任务
分享第四单元的主题是正向建模与UML驱动开发。与前三个单元截然不同——第一单元是"从零构建表达式引擎",第二单元是"多线程电梯调度",第三单元是"JML规格驱动实现"——本单元的学习目标是掌握从UML模型出发的正向建模方法,即先设计类图、状态图、顺序图,再依据模型实现代码。核心体验是:"模型即蓝图,代码是模型的忠实映射"。
本单元以"图书馆管理系统"为场景,经历了三次作业的迭代:
HW13:基础图书馆流通系统,借书/还书/预约/取书/查询,初次实践两阶类图的正向建模方法。
HW14:引入精品书架、阅览室、评分系统,新增状态机(State Machine)建模,架构复杂度跃升。
HW15:引入信用分系统、续借、逾期扣分,新增顺序图(Sequence Diagram)建模,功能体系趋于完整。
HW13 的核心任务不仅是实现功能,更是体验两阶类图的正向建模流程:
一阶类图(uml_pre.mdj):在阅读需求后、编写代码前,先设计初步的类结构。此时我采用了 Command 模式——设计了 Request 抽象基类及其五个子类(BorrowRequest、ReturnRequest、OrderRequest、PickRequest、QueryRequest),由 LibraryManager.processRequest() 多态分发。这个阶段的核心目标是快速建立领域模型,将需求中的实体(用户、图书、书架、借还处、预约处)映射为类。
终极类图(uml_ultimate.mdj):在代码实现完成后,根据实际代码结构对一阶类图进行重构。经过实践,我发现了 Command 模式在此场景下的问题——每个 Request 子类只包含极少的差异化逻辑,继承体系带来的抽象收益远小于其复杂度成本。于是进行了结构性重构:消除 6 个 Request 子类及其继承体系,将操作逻辑内聚到 Library 类中,直接暴露 borrow()、returnBook()、order()、pick()、query() 方法。
两阶类图的核心价值在于:一阶类图是"设计的假设",终极类图是"实践的总结"——两者之间的差异正是设计思维成熟的过程。
HW13 最终采用基于位置的委托架构(Location-Based Delegation):
Main (入口层) └── Library (Facade / 协调层) ├── Bookshelf — 书架(FIFO队列) ├── BorrowReturnOffice — 借还处 ├── AppointmentOffice — 预约处(LinkedHashMap保序) ├── Map<String, User> — 用户管理 └── Map<LibraryBookId, BookCopy> — 全局副本索引
核心类职责:
| 类名 | 职责 | 关键设计 |
|---|---|---|
Library | 外观模式协调器,管理所有业务操作 | 开馆/闭馆整理、预约履约、借还调度 |
BookCopy | 图书副本,自身维护状态与轨迹 | moveTo() 实现状态转移 + 轨迹记录 |
Order | 预约订单生命周期管理 | 状态机:PLACED → FULFILLED → COMPLETED/EXPIRED |
User | 用户借阅规则校验 | 封装 A/B/C 三类书的差异化借阅策略 |
Bookshelf | 按ISBN分组的FIFO队列 | ArrayDeque 保证先入库先借出 |
BorrowReturnOffice | 还书中转站 | 简单列表容器 |
AppointmentOffice | 预约处管理 | LinkedHashMap 保证插入顺序 |
| 决策维度 | 一阶类图(Command模式) | 终极类图(Facade模式) |
|---|---|---|
| 请求处理 | 6个Request子类 + processRequest()多态 | Library直接方法:borrow/returnBook/order/pick/query |
| 类数量 | 15个类 | 9个类 |
| 扩展性 | 新增操作需新增子类 | 新增操作在Library加方法 |
| 可读性 | 分散在多个类中 | 集中在Library中,流程清晰 |
重构动机:Command 模式在此场景下属于过度设计。每个 Request 子类仅包含 1-2 行差异化逻辑,继承体系的抽象成本远超其收益。Facade 模式更贴合"图书馆"这一领域——所有操作都是图书馆这个聚合根的行为,不存在"命令对象"的独立生命周期。
预约在开馆/闭馆整理时统一履约,而非用户下单时实时处理。这是关键的架构决策:
开馆前履约:有效期 4 天(fulfillDate + 4)
闭馆后履约:有效期 5 天(fulfillDate + 5)
过期后自动将图书从预约处移回书架
HW14 在 HW13 基础上新增了:
精品书架(Treasured Bookshelf):通过评分系统(平均分 >= 4)动态分类
阅览室(ReadingRoom):用户可在馆阅读,读完后归还
评分系统(grade):用户对图书评分,影响精品分类
状态机建模:为 BookCopy 设计完整的状态转移图
Main (命令解析层) └── LibraryManager (业务调度层) ├── Bookshelf (bs) — 普通书架 ├── Bookshelf (tbs) — 精品书架 ├── BorrowReturnOffice — 借还中转站 ├── AppointmentOffice — 预约管理处 ├── ReadingRoom — 阅览室 ├── Map<String, User> — 用户管理 └── Map<ISBN, List<Integer>> — 评分数据
核心新增功能:
双书架 + 动态分类:classifyBooks() 在开馆整理时执行,双向扫描 bs 和 tbs,根据 isTreasured() 判断结果迁移图书。使用 removeSpecific() 而非 remove() 来精确移除指定副本。
阅览流程:read() 从书架取书放入阅览室 → restore() 从阅览室取出放入借还处 → 开馆/闭馆整理时从借还处归架。用户同时只能读一本书。
预约履约增强:履约时从 bs 和 tbs 两个书架查找,优先从 bs 取书。
HW14 的核心突破是引入了状态机(State Machine)建模。为 BookCopy 设计了 7 个状态 + 14 条状态转移:
| 源状态 | 目标状态 | 触发器 | 业务场景 |
|---|---|---|---|
| InitState | bs | init() | 初始化入库 |
| bs | user | borrow() | 借阅 |
| tbs | user | borrow() | 从精品书架借阅 |
| bs | rr | read() | 阅览 |
| tbs | rr | read() | 从精品书架阅览 |
| bs | ao | fulfill() | 预约履约 |
| tbs | ao | fulfill() | 从精品书架履约 |
| bs | tbs | classifyToTbs() | 升级为精品 |
| tbs | bs | classifyToBs() | 降级为普通 |
| user | bro | returnBook() | 归还 |
| rr | bro | restore() | 归还阅览 |
| bro | bs | arrangeToBs() | 整理到普通书架 |
| bro | tbs | arrangeToTbs() | 整理到精品书架 |
| ao | bs | expire() | 预约过期 |
设计要点:所有从同一状态出发到不同目标的转移,Guard 条件互斥——通过 targetState 辅助变量实现。例如从 bs 出发有 4 条转移(borrow→user, read→rr, fulfill→ao, classifyToTbs→tbs),它们的 targetState 各不相同,保证了转移的确定性。
| 维度 | 一阶类图 | 终极类图 |
|---|---|---|
| BookCopy | 无 targetState | 新增 targetState + 12个@Trigger方法 |
| Order | 单一 isExpired() | 拆分为 isExpiredOnOpen() + isExpiredOnClose() |
| AppointmentOffice | 单一 removeExpired() | 拆分为 removeExpiredOnOpen() + removeExpiredOnClose() |
| 状态图 | 无 | 新增 StateMachine1(7状态+14转移) |
重构原因:一阶类图设计时,对"开馆/闭馆整理时的过期判断差异"理解不够深入。代码实现后,发现需要精确区分 isAfter(开馆)和 !isBefore(闭馆)两种过期语义,因此将方法拆分。状态图也是在一阶类图之后才设计的,因为一阶类图阶段对状态转移的全局视图掌握不足。
HW15 在 HW14 基础上新增了:
信用分系统:初始 100 分,上限 180,下限 0
续借(renew):逾期前可续借 7 天
逾期扣分机制:按时归还 +10,逾期 -15,预约过期 -15,闭馆未还 -10/本
信用分分级权限:>80 可借阅/预约,>40 可读 A 类,>0 可读其他
顺序图建模:为核心业务流程设计时序交互图
HW15 的架构在 HW14 基础上增量演进,核心变化集中在 LibraryManager 和 BookCopy:
逾期扣分去重:BookCopy 新增 overdueDeducted 标记,processOverdueDeductions() 在开馆/闭馆时统一处理,防止同一逾期事件被重复扣分。
预约履约四级查找:fulfillOrdersFromAll() 从 bro → rr → bs → tbs 四级优先查找,而非仅从 bs/tbs。这保证了归还到借还处的书也能第一时间满足预约。
预约排序:按 placedDate 升序 + seq 降序排序,确保先预约的用户优先得到履约,同时保证确定性。
归还验证:returnBook() 和 restore() 增加了所有权验证(hasBorrowedCopy() / isReadingBook()),防止归还不属于自己的书。
续借逻辑:renew() 在逾期前可延长 dueDate 7 天,逾期后不可续借。
HW15 新增了顺序图(Sequence Diagram),为核心业务流程建模对象间的时序交互。以"借书"流程为例:
User → LibraryManager → Bookshelf(bs) → Bookshelf(tbs) → BookCopy → User | | | | | | | borrow | | | | | |-------->| remove(isbn) | | | | | |-------------->| | | | | | (null) | | | | | |<--------------| remove(isbn) | | | | |------------------------------>| | | | | copy | | | | |<------------------------------| | | | | borrowBook(date,days) | | | |---------------------------------------------->| | | | moveTo(USER, date) | | | |---------------------------------------------->| | | | addBorrowedBook(copy) | | | |---------------------------------------------------------------->| | accept(cmd, bookId) | | | | |<--------| | | | |
顺序图的核心价值在于将类图中的静态关系转化为动态交互:类图告诉我们"谁和谁关联",顺序图告诉我们"谁先调用谁,参数是什么,返回值是什么"。
截至 HW15,UML 模型包含三类图:
| 图类型 | 内容 | 建模对象 |
|---|---|---|
| 类图(Class Diagram) | 9个核心类 + 关联关系 + 属性/方法 | 系统的静态结构 |
| 状态机(State Machine) | 7个状态 + 14条转移 + Trigger + Guard | BookCopy 的生命周期 |
| 顺序图(Sequence Diagram) | 多条生命线 + 消息传递 | 核心业务流程的时序交互 |
一阶类图是"先设计,后编码"的关键产出。它的核心价值不在于"正确",而在于"快速暴露设计问题"。
在 HW13 的一阶类图中,我采用了 Command 模式。这个设计在画图阶段看起来合理——每种操作对应一个 Request 子类,符合开闭原则。但当我开始编码时,立刻发现了问题:每个子类的 process() 方法只有 1-2 行,继承体系的复杂度远大于其带来的收益。如果没有一阶类图这个"设计快照",我可能会在代码中陷得更深,重构成本更高。
一阶类图的作用总结:
领域建模:将需求中的概念映射为类和关系,建立对问题域的初步理解
设计实验:低成本尝试不同的架构模式,在纸上(或工具中)而非代码中试错
沟通工具:作为团队讨论的基础,让设计意图可视化
终极类图是"编码后,回过头来修正设计"的产物。它记录的不仅是"代码是什么",更是"为什么最终选择了这个设计"。
在 HW14 中,终极类图新增了 targetState 属性和 12 个 @Trigger 方法。这些是在编码过程中才发现的需求——状态机的 Guard 条件需要辅助变量来表达互斥性。如果一开始就试图在类图中设计这些细节,反而会陷入过度设计。
终极类图的作用总结:
设计文档化:为后续维护者(包括未来的自己)提供精确的代码蓝图
设计审查:通过对比一阶类图和终极类图,发现设计决策的演变轨迹
模型验证:确保代码结构与 UML 模型一致,作为自动化评测的基础
两阶类图之间的差异,恰恰是正向建模最有价值的部分:
| 差异类型 | 一阶类图 | 终极类图 | 启示 |
|---|---|---|---|
| 架构模式 | Command 模式 | Facade 模式 | 设计模式应服务于领域,而非教条 |
| Order 过期判断 | 单一 isExpired() | 拆分为 OnOpen/OnClose | 编码会暴露设计阶段的模糊假设 |
| BookCopy 状态管理 | 无 targetState | 有 targetState + @Trigger | 工具链需求(状态机评测)会反推设计调整 |
| 职责划分 | 借阅限制在 BorrowLimit | 合并入 User | 信息专家原则:数据在哪里,行为就在哪里 |
本单元的类图与代码之间保持了高度一致的追踪关系。以下是核心类的映射:
| UML 类图中的类 | 代码中的类 | 一致性 | 差异说明 |
|---|---|---|---|
| Library / LibraryManager | LibraryManager.java | 高 | 类图早期用 Library,代码用 LibraryManager,语义一致 |
| BookCopy | BookCopy.java | 高 | 属性、方法、@Trigger 全部对应 |
| Bookshelf | Bookshelf.java | 高 | copies 结构(Map<ISBN, Deque>)完全一致 |
| BorrowReturnOffice | BorrowReturnOffice.java | 高 | - |
| AppointmentOffice | AppointmentOffice.java | 高 | - |
| ReadingRoom | ReadingRoom.java | 高 | - |
| Order | Order.java | 高 | State 枚举 + 状态转换方法完全一致 |
| User | User.java | 高 | 借阅规则 + 信用分方法完全一致 |
状态机中的每个状态、转移、Trigger、Guard 在代码中都有对应的实现:
状态 → LibraryBookState 枚举值(BOOKSHELF、TREASURED_BOOKSHELF、USER、BORROW_RETURN_OFFICE、APPOINTMENT_OFFICE、READING_ROOM)
转移 → BookCopy.moveTo() 方法调用,自动记录轨迹
Trigger → BookCopy 中 12 个包级私有的 @Trigger 注解方法
Guard → targetState 属性的值,在每次 moveTo() 时自动更新
这种映射关系保证了状态机不是"画完就扔"的文档,而是与代码同步演进的活模型。
顺序图中的生命线(Lifeline)对应代码中的对象实例,消息(Message)对应方法调用。以"借书"为例:
| 顺序图元素 | 代码对应 |
|---|---|
| :User → :LibraryManager.borrow() | libraryManager.borrow(date, studentId, isbn) |
| :LibraryManager → :Bookshelf.remove(isbn) | bs.remove(isbn) |
| :LibraryManager → :BookCopy.borrowBook(date, days) | copy.borrowBook(date, days) |
| :LibraryManager → :BookCopy.moveTo(USER, date) | copy.moveTo(LibraryBookState.USER, date) |
| :LibraryManager → :User.addBorrowedBook(copy) | user.addBorrowedBook(copy) |
保持 UML 模型与代码之间的追踪关系,带来了以下收益:
自动化评测:课程评测框架可以直接读取 UML 模型文件,验证代码实现是否与模型一致。HW15 中大量的 fix_*.py 脚本正是为了修复模型与代码之间的不一致。
设计一致性:当需要修改代码时,可以先检查 UML 模型,确认修改是否影响其他部分。例如,新增 renew() 功能时,需要同步更新类图(BookCopy 新增 renew() 方法)和顺序图(新增续借流程)。
知识传递:UML 模型比代码更直观,新成员可以通过阅读模型快速理解系统架构。
在本单元中,我大量使用大模型(GLM/Claude)辅助正向建模的各个环节:
UML 模型生成:将自然语言需求描述输入大模型,生成初始的类图、状态机、顺序图结构(.mdj JSON 文件)。
模型修复:当评测框架报告 UML 模型与代码不一致时,编写 Python 脚本(如 fix_uml_issues.py、fix_seq_back.py)自动修复模型文件。大模型在生成这些修复脚本方面效率极高。
代码生成:基于 UML 类图,让大模型生成方法骨架,人工填充业务逻辑。
模型验证:将 .mdj 文件和 .java 文件一起提交,让大模型检查两者的一致性。
经过本单元的实践,我总结出以下引导策略:
策略一:分层递进,逐步细化
不要一次性让大模型输出完整设计。应该分步骤:
先让大模型输出领域模型(有哪些实体,它们之间有什么关系)
再让大模型输出类图(每个类的属性和方法)
最后让大模型输出行为模型(状态机、顺序图)
在 HW14 中,我先让大模型分析"图书馆有什么?书架、借还处、预约处、阅览室、用户、图书...它们之间怎么交互?",等它输出领域模型后,再让它细化每个类的属性和方法。
策略二:提供约束和上下文
大模型在缺乏约束时容易"自由发挥"。在引导时,需要明确提供:
外部库的 API(如 LibraryBookId、LibraryBookState 的类型定义)
已有的代码结构(如 LibraryManager 的现有方法签名)
特定的设计约束(如"必须使用 Facade 模式"、"预约履约在开馆整理时统一处理")
在 HW15 中,我让大模型基于 HW14 的代码生成 HW15 的增量设计,它能够准确识别需要新增的类、方法和属性,避免了重复设计。
策略三:利用大模型进行"设计评审"
将一阶类图输入大模型,让它扮演"设计评审者"的角色,指出潜在问题:
"这个 Command 模式是否过度设计?"
"Order 的过期判断是否需要区分开馆和闭馆?"
"BookCopy 的状态机是否完备?"
在 HW13 中,大模型指出了 Command 模式的过度设计问题,这与我在编码中的实际感受一致,加速了重构决策。
策略四:脚本化批量操作
UML 模型文件(.mdj)是 JSON 格式,大模型非常适合生成和修改 JSON 结构。在 HW15 中,我大量使用大模型生成 Python 脚本:
build_uml.py:完全重建类图中的所有属性和方法
fix_seq_back.py:修复顺序图中的消息方向
update_uml.py:为模型增量添加新方法
这种"大模型生成脚本 → 脚本修改模型 → 人工验证"的工作流,比手动编辑 .mdj 文件高效得多。
优势:
快速原型:从需求到初始类图,大模型可以在几分钟内输出一个可用的设计草案,人工只需调整和细化。
批量操作:修改 .mdj JSON 结构时,大模型生成的 Python 脚本可以一次性处理几十个字段,比手动编辑快 10 倍以上。
一致性检查:大模型可以同时阅读 UML 模型和 Java 代码,检查两者之间的不一致,这是人工容易遗漏的。
局限:
缺乏领域深度理解:大模型对"图书馆业务流程"的理解停留在表面,不会主动考虑"开馆履约和闭馆履约的过期时间应该不同"这样的领域细节。这些细节需要人工补充。
倾向于过度设计:在没有明确约束时,大模型倾向于使用各种设计模式(Command、Strategy、Observer),导致架构过于复杂。需要人工做"减法"。
生成代码的"模型一致性"不足:大模型生成的代码往往与它自己生成的 UML 模型不完全一致——例如类图中定义了 targetState 属性,但生成的代码中没有使用它。这种不一致需要通过模型验证环节来发现。
第一单元的主题是表达式化简。我的架构经历了从扁平的多项式模型(Poly + HashMap)到树形的 AST 模型(Expr 继承体系)的转变。
核心收获:当问题域中存在"部分-整体"的层级结构时(如表达式中的嵌套括号),应采用组合模式(Composite Pattern)构建树形结构,而非强行扁平化。扁平化在简单场景下高效,但面对复杂嵌套时必然崩溃。
第二单元的主题是多线程电梯调度。我的架构从"一个类搞定一切"的粗暴设计演进为生产者-消费者模式的多线程协作架构。
核心收获:并发编程的核心不是"让代码跑得快",而是"明确每个线程的职责边界"。InputThread 只负责读取输入,ScheduleThread 只负责调度策略,ElevatorThread 只负责执行移动。线程间的通信通过共享的 RequestQueue 完成。这种职责分离 + 消息传递的思维,在后续单元中持续影响着我。
第三单元的主题是 JML 规格驱动开发。架构设计不再是"我想怎么设计",而是"规格规定了什么接口,我需要如何实现它们"。
核心收获:契约式设计(Design by Contract)的思想——实现者只需且必须精确满足规格,任何额外的"智能"都是潜在的 bug。HW10 的三个 bug 全部源于"我觉得应该这样"的额外假设。这让我认识到:在规格驱动的场景下,架构设计的自由度被规格约束,但质量也被规格保障。
第四单元的主题是正向建模。与前三个单元"先写代码,再画图(或干脆不画图)"不同,本单元要求先设计 UML 模型,再编写代码。
核心收获:模型是设计的"压缩表示"。一个好的 UML 类图比 100 行代码注释更能传达设计意图。两阶类图的方法论——一阶类图作为设计假设,终极类图作为实践总结——让我认识到:设计不是一次完成的,而是在"假设→验证→修正"的循环中逐步收敛的。
| 单元 | 设计范式 | 核心思维转变 |
|---|---|---|
| 第一单元 | 递归下降 + AST | 从扁平化到树形结构,理解组合模式 |
| 第二单元 | 生产者-消费者 | 从单线程到多线程协作,理解职责分离 |
| 第三单元 | 契约式设计 | 从"我想做"到"规格要我做什么",理解规格的精确性 |
| 第四单元 | 正向建模 | 从"代码先行"到"模型先行",理解设计先于实现 |
贯穿四个单元的核心线索是:架构设计的能力本质上是对"复杂度"的管理能力。第一单元管理的是表达式嵌套的复杂度,第二单元管理的是并发协作的复杂度,第三单元管理的是规格精确性的复杂度,第四单元管理的是模型与代码一致性的复杂度。
第一单元的测试策略比较原始:手工构造边界表达式(如 0^0、x^0、(x+1)^50 等)+ Python 随机生成器。测试目标是"输出对不对",验证方式是"目测 + 与同学对拍"。
局限性:缺乏系统性的测试方法论,测试用例的覆盖度不可控,bug 往往在互测阶段才被发现。
第二单元引入了新的测试维度:时序正确性。除了验证"电梯最终到达了正确的楼层",还需要验证"线程安全"(无死锁、无竞态条件)。
核心策略:
编写压力测试脚本,生成大量随机请求,观察程序是否出现死锁或线程饿死
使用时间戳日志记录每个请求的生命周期,验证"先到先服务"的调度策略
人工构造边界时序(如同一时刻多个请求到达)
收获:并发测试比串行测试难得多——同样的输入可能因为线程调度的不确定性产生不同的输出。测试策略需要从"验证输出"转向"验证不变量"(如"电梯不会同时向两个方向移动")。
第三单元正式引入了JUnit 单元测试和规格驱动的测试方法。
核心策略:
为每个 JML 方法的 requires/ensures/signals 子句生成对应的测试用例
使用 strictEquals 深度验证对象的所有字段,而不仅仅是 id
使用大模型生成随机测试脚本,覆盖大量边界组合
收获:规格驱动的测试比"拍脑袋"测试更系统、更可靠。JML 的 ensures 子句直接给出了测试的预期结果,signals 子句直接给出了异常测试的触发条件。测试的"输入-预期输出"映射是由规格保证的,而非测试者主观判断。
第四单元的测试维度发生了根本性变化:不仅要验证代码的正确性,还要验证代码与 UML 模型的一致性。
核心策略:
模型一致性测试:编写 Python 脚本(如 check_seq_views.py、check_statemachine.py、check_structure.py)自动检查 .mdj 文件与 Java 代码之间的一致性
场景覆盖测试:为每个业务场景编写测试用例(如 test_borrow_return_cycle.txt、test_expired_reorder.txt、test_treasured_late_return.txt)
边界值测试:信用分 0/40/80/100/180 的边界、逾期 0 天/1 天/7 天的边界、预约过期 4 天/5 天的边界
大规模压力测试:test_massive.txt 生成海量操作序列,验证性能和正确性
收获:模型一致性测试是第四单元独有的测试维度。在 HW15 中,我编写了大量 check_*.py 和 fix_*.py 脚本,形成了"检查 → 发现不一致 → 修复 → 再检查"的自动化测试循环。这让我认识到:测试不仅是"找代码的 bug",也可以是"找设计的 bug"——当 UML 模型与代码不一致时,要么是代码写错了,要么是模型设计有缺陷,两者都需要修正。
| 单元 | 测试方法 | 测试维度 | 核心思维转变 |
|---|---|---|---|
| 第一单元 | 手工 + 随机 | 输出正确性 | 从"目测"到"对拍" |
| 第二单元 | 压力测试 + 日志 | 时序正确性 + 线程安全 | 从"验证输出"到"验证不变量" |
| 第三单元 | JUnit + 规格覆盖 | 规格一致性 | 从"拍脑袋"到"规格驱动" |
| 第四单元 | 模型验证 + 场景覆盖 | 代码正确性 + 模型一致性 | 从"测代码"到"测设计" |
贯穿四个单元的核心线索是:测试的层次不断提升——从"功能对不对"(第一单元)到"并发安全吗"(第二单元)到"规格满足吗"(第三单元)到"设计一致吗"(第四单元)。每一层都是对前一层的补充,而非替代。
经过四个单元的训练,我最大的收获是"设计先于实现"的意识。第一单元时,我拿到需求就开始写代码,结果第二次作业不得不大规模重构。到了第四单元,我已经习惯先画 UML 类图,确认架构合理后再动手编码。这种"慢即是快"的体验,是 OO 课程给我最宝贵的财富。
好的架构不是"功能最多的架构",而是"在满足需求的前提下,复杂度最低的架构"。HW13 从 Command 模式重构为 Facade 模式,代码量减少了 40%,可读性大幅提升——这就是"做减法"的价值。过度设计与设计不足同样有害。
UML 模型不是"为了画图而画图",而是有实际用途的设计工具。类图帮助我理清静态结构,状态机帮助我理清对象生命周期,顺序图帮助我理清对象间交互。当模型与代码保持一致时,模型就是最好的文档。
大模型在生成代码框架、修复模型文件、检查一致性方面效率极高,但它无法替代人的设计判断。架构设计的核心决策——选择什么模式、如何划分职责、哪些细节需要抽象——仍然需要人来完成。大模型的价值在于消除机械劳动,让人专注于创造性思考。
没有哪个设计是"一次做对"的。一阶类图到终极类图的变化、HW13 到 HW15 的三次迭代、第一单元到第四单元的四次思维转变——都在说明同一个道理:好的设计是在迭代中逐步收敛的,而不是在初始阶段就完美的。
回顾整个 OO 课程,四个单元构成了一个完整的"面向对象能力培养链":
第一单元教会我如何用对象建模复杂的数据结构(表达式树)
第二单元教会我如何用对象建模复杂的并发行为(电梯调度)
第三单元教会我如何在规格约束下实现对象(JML 契约)
第四单元教会我如何在编码之前设计对象(UML 正向建模)
从"先写代码"到"先画模型",从"想怎么做就怎么做"到"规格说怎么做就怎么做",从"功能正确就行"到"设计也必须正确"——这一路走来,我学到的不仅是 Java 编程和 UML 画图,更是一种面向对象的思维方式:将复杂问题分解为协作的对象,让每个对象有清晰的职责和边界,在迭代中持续优化设计。