309
社区成员
发帖
与我相关
我的任务
分享随着本周最后一次研讨课暨颁奖典礼落幕,这学期的面向对象课程终于画上了圆满的句号。回首这十六周的旅程:第一单元我们在文法解析中摸爬滚打,第二单元在与多线程作战,第三单元在 JML 契约下优化性能,而到了第四单元,我们体验了一把先画图纸,再盖大楼的正向建模开发流程。
本文将以第四单元的实践为主线,串联前三个单元的思考,全面总结架构设计与测试思维的演进。
在过去的编程习惯中,我往往是拿到指导书就开始“面向键盘编程”,一边写、一边想、一边重构,最后为了博客交差,再用 IDEA 插件逆向出一个 类图。然而,本单元引入的两阶类图机制影响了我的开发流程。
在动手敲下第一行 Java 代码前,我先在 StarUML 中完成一阶类图(uml_pre.mdj):
Location,统一维护 books 容器。LibraryManager 作为全局控制器,包含 Map<String, User> userMap 和各类 Location,统筹 borrow、return、order 等所有核心逻辑。
此时的方法设计还比较粗略(例如只有 +getAvailableBook() 的空括号,没有细化参数和返回值类型),此时的设计更多是头脑风暴的具象化。
在具体的代码实现过程中,我发现一阶类图存在业务覆盖的盲区,于是进行了针对性的重构并在二阶类图(uml_ultimate.mdj)中同步更新

BorrowRecord 实体:在增加借阅期限和信用分系统后,简单的 Map<LibraryBookId, BookCopy> 已经无法满足需求。我重构加入了 BorrowRecord 类,用于精准封装 deadline 和 penalized(是否已扣过分)状态,使得 User 类的职责更加内聚。+getAvailableBook(isbn: LibraryBookIsbn): BookCopy,将静态方法 main 加上了下划线并补充了 : void 返回值。在第四单元中,代码是严格顺应 UML 模型的投影。
在我的代码中,UML 类图的连线几乎做到了 100% 的代码级追溯:
public class AppointmentOffice extends Location 严格对应类图中的泛化箭头。User 到 BorrowRecord 的实线关联,在代码中真实地体现为 private Map<LibraryBookId, BorrowRecord> borrowedCBooks。我的状态图

围绕着一本书(BookCopy)的生命周期展开。在代码侧,我利用 @Trigger 注解实现了模型与代码的咬合:
@Trigger(from = "bs", to = "user")
@Trigger(from = "tbs", to = "user")
private void handleBorrow(LibraryReqCmd cmd, User u) { ... }
公测逃课:为了满足评测机“任意简单路径必定有解(R3)”以及“同源外出 Guard 必须互斥”的要求,我在绘制状态图时让每个状态的出口绑定不同且独立的业务变量(如 bs 出发全用 credit 判定,tbs 全用 limit)。这在逻辑上 100% 避免了类似 limit==1 && limit==2 的路径死锁冲突。
顺序图

刻画了 Endpoint、User、LibraryManager 和 AppointmentOffice 的交互。
在绘制时,我深刻领悟了 R3 规则(跨类消息对应方法必须为 public)。我放弃了让 User 直接调用 LibraryManager.handleOrder(因为它是 private),而是通过调用其公共枢纽方法 dispatchCommand(),再由其分发至内部私有方法。这种符合封装特性的设计,让消息链的传递不仅合法,而且在面向对象原则上更加优雅。
在这一单元,我体验了大模型(Gemini)在架构设计中的辅助作用,可以用惊艳但不可盲信来形容。
LibraryManager 中二十几个方法的签名,精准翻译为符合 UML 规范的文本串(例如将 private void handleOrder(LibraryReqCmd cmd, User u) 翻译为 -handleOrder(cmd: LibraryReqCmd, u: User): void),极大地提升了建模效率。.mdj JSON 文件中加入 "ownedViews": [] 这种空视图数组,这会触发 StarUML 的 Bug 导致整个画布凭空消失。通过这些踩坑,我总结出一个 Prompt 策略:
绝不能只丢给它一句“帮我画个顺序图”,而是要把上下文约束作为 System Prompt 强行注入。例如:
“你现在是一个极其严格的 UML 架构师。请根据我的 Java 代码生成顺序图交互链。要求:1. 相邻两条消息的 Source 和 Target 必须首尾相接;2. 跨类调用的方法必须在代码中声明为
public;3. 请用Endpoint作为初始消息发送者……”
只有给出这样精确的边界条件,AI 才能成为真正好用的架构利器。
回首四次大战,我的架构设计思维经历了四次涅槃:
Calculate 类。经历重构后,我学会了提取 Monomial(单纯的数据载体)和 Poly(纯粹的行为载体),并利用工厂模式实例化各种 Factor,初窥了“高内聚低耦合”的门径。GlobalContext 作为共享资源池,引入 Dispatcher 作为统筹大脑,而 Elevator 退化为只管按指令移动的机器。这种“策略与执行剥离”的架构,让双轿厢机制(HW7)的接入变成了轻而易举的事。mutualFollowingSum 必定 TLE。我开始在架构层面引入动态维护和缓存机制(如使用 LinkedHashMap 保证顺序,维护分桶结构优化 queryMostPopularVideo),让算法复杂度从 O(N^2) 降维到 O(1)。随着业务复杂度的提升,我的测试手段也从原始的黑盒逐渐走向了体系化的沙箱测试:
assignable)比测试返回值更重要。在 JUnit 中,我会在方法执行前后为对象拍“快照”,确保所有未被授权修改的容器大小和状态绝对没有发生越权污染。credit < 80 进行拦截(这意味着 80 分被放行了)。在后期的沙箱推演中,我专门挑出临界点(0, 40, 80)输入,成功在提交前猎杀了这个必挂强测的高危 Bug。LinkedHashMap,从数据结构层面把测试的不确定性彻底摁死。历经十六周的魔鬼训练,OO 课程带给我的不止是 Java 代码的积累。
一学期的面向对象之旅固然熬人,但在最后一次强测看到满屏绿色时,所有的疲惫都化作了无可替代的成就感。感谢 OO,让我踏进了面向对象设计的大门。
再见,2026 OO!杀青!