BUAA-OO-Unit4-总结

罗春宇-22371220 2025-06-11 10:46:51

# 从零到一:面向对象设计与构造的建模、架构与反思

## 前言

随着第四单元——UML建模——的落幕,2025年的OO课程也画上了句号。这门课程带领我们走过了一段精彩纷呈的旅程:从第一单元表达式化简的计算思维,到第二单元多线程电梯的并发共舞,再到第三单元JML规格驱动的契约式开发,最终在第四单元,回归设计的本源,通过UML进行正向建模与开发。

本文旨在总结与复盘我在第四单元中关于UML建模与程序设计的实践,并以此为契机,回顾整个学期在架构设计与测试思维上的演进,同时探讨在新时代下,如何利用大语言模型(LLM)辅助我们进行复杂的软件设计,最后总结这门课程带给我的宝贵收获。

## 一、 单元总结:正向建模的实践之旅

本单元的核心是**正向建模与开发**,即“先设计,后编码”。我们借助UML的三种核心图表,分三次作业,逐步构建了一个功能日益复杂的图书馆管理系统。

1.  **UML类图 (Class Diagram)**:在HW13中,我们初次接触图书馆系统,核心任务是利用类图来描绘系统的静态结构。这要求我们识别出系统的核心实体,如`Library`(图书馆)、`Student`(学生)、`BookCopy`(书籍副本),并定义它们各自的属性、方法以及它们之间的关系(关联、聚合、依赖)。这为整个系统的骨架打下了坚实的基础。

2.  **UML状态图 (State Diagram)**:HW14引入了热门书架和阅览室等新概念,使得一本书籍副本(`BookCopy`)的生命周期变得更加复杂。状态图成为了描述`BookCopy`在不同位置(如普通书架、热门书架、借还处、阅览室、用户手中)之间流转的完美工具。通过定义状态(State)、迁移(Transition)、触发器(Trigger)和监护条件(Guard),我们能够清晰、无歧D义地刻画出单个对象在响应外部事件时的动态行为。

3.  **UML顺序图 (Sequence Diagram)**:HW15引入了信用分和借阅期限,使得用户与图书馆的交互逻辑,以及图书馆内部各部门的协作流程变得更为精细。顺序图让我们能够聚焦于一个具体的业务场景(如“学生成功预约图书”),通过生命线(Lifeline)和消息(Message)来展示对象之间随着时间推移的交互顺序。这帮助我们验证和梳理了系统在处理复杂请求时的动态协作关系。

本单元通过一个迭代递进的项目,让我们亲身体验了如何运用UML将模糊的需求转化为清晰、可视化的设计蓝图,并以此为指导进行编码,有效地降低了开发的复杂度和后续的维护成本。

## 二、 架构设计:构建数字图书馆

根据三次作业的需求和最终代码,我的系统架构设计如下,这套设计也基本遵循了最初的UML类图规划。

*   **`Main`**:作为程序的入口和控制器(Controller)。它负责接收官方包解析后的指令,并将其分发给`Library`类进行处理。它本身不包含任何业务逻辑,仅作为用户界面与核心逻辑之间的桥梁。

*   **`Library`**:系统的核心管理者,扮演了**外观(Facade)**的角色。它对`Main`类隐藏了系统内部的复杂性,提供了统一的接口来处理所有业务请求,如`borrowBook`, `returnBook`, `organizeBooks`等。`Library`内部维护了所有学生和所有书籍副本的集合,是整个系统的中心协调者。
    *   `Map<LibraryBookId, BookCopy> allBookCopies`: 存储所有书籍副本,通过唯一ID快速检索。
    *   `Map<LibraryBookIsbn, List<BookCopy>> booksByIsbn`: 按ISBN对书籍进行分组,便于查找某一ISBN下的可用副本。
    *   `Map<String, Student> students`: 存储所有学生对象,通过学生ID快速访问。

*   **`Student`**:代表学生实体。它封装了与学生相关的所有状态和行为,包括学生ID、信用分、当前持有的书籍列表、预约信息、在阅览室的书籍等。`Student`类的设计体现了良好的封装性,其内部状态的变更都通过定义好的方法进行,如`addCredit`, `reduceCredit`, `addHeldBook`等。

*   **`BookCopy`**:代表每一本物理存在的书。这是本单元**状态模式**的核心实践对象。它的`currentLocation`属性直接对应状态图中的一个状态。`moveTo`方法是实现状态迁移的核心,记录了书籍的移动轨迹。其他属性如`dueDate`, `reservedForStudentId`则保存了与特定状态相关的上下文信息。

*   **`BorrowedBookInfo`**:一个简单的数据类(POJO)。它被用来封装一本被借出的书籍的特定信息,如图书的ISBN、借阅日期和应还日期。将这部分信息从`BookCopy`中分离出来,使得`Student`可以独立跟踪自己借阅历史的详细信息,降低了`BookCopy`和`Student`之间的耦合。

### 模型与代码的追踪关系

将最终代码与UML模型进行对比,可以发现高度的一致性:

*   **类图与代码**:最终的`Library`, `Student`, `BookCopy`等核心类,其名称、核心属性和公共方法,都与最初设计的类图保持了高度一致。代码中的关联关系,如`Library`聚合`Student`和`BookCopy`,`Student`持有`BookCopy`的ID,也都在类图中得到了体现。一些在开发过程中增加的私有辅助方法或中间变量,虽然未在顶层设计图中出现,但这属于实现层面的细节优化,不影响整体架构,是模型向具体实现演进的正常现象。

*   **状态图与代码**:`BookCopy`类的设计与状态图的追踪关系最为紧密。`LibraryBookState`枚举类(或等效常量)直接定义了所有状态。`moveTo()`方法成为了实现状态迁移的统一入口,其`@Trigger`注解明确宣告了它在状态机中的角色。而分散在`Library`类中各个业务方法(如`borrowBook`, `organizeBooks`)里的逻辑判断,则构成了状态迁移的**监护条件(Guard)**。例如,只有当学生信用分足够且书在书架上时,借书操作才能触发书本从“书架”到“用户”的状态迁移。

*   **顺序图与代码**:以“预约图书”为例,顺序图描绘了`Student`发起`orderNewBook`消息给`Library`。在代码实现中,`Library`的`orderNewBook`方法会检查学生的资格并记录预约意向(ISBN)。之后,在`organizeBooks`方法中,图书馆会遍历有预约需求的学生,查找可用书籍,并通过`BookCopy`的`moveTo`方法将其移至预约处,同时更新学生和书籍的状态。这一系列调用链虽然不是在`orderNewBook`一个方法内同步完成,但逻辑上与顺序图描绘的对象交互流程是完全吻合的。`@SendMessage`注解帮助我们从代码中反向追溯这种逻辑上的消息传递。

## 三、 与AI共舞:引导大模型进行复杂架构设计

在本次作业中,我尝试利用大语言模型(DeepSeek-R1)辅助设计。体验下来,我认为,AI相当于辅助驾驶,能够轻松化驾驶过程,但方向盘必须始终掌握在人类开发者手中。要让AI在复杂场景中完成高质量的架构设计,关键在于**引导**而非**命令**。

1.  **分步拆解与增量提示 (Decomposition & Incremental Prompting)**:
    *   **错误方式**:“请为我设计一个图书馆系统,满足HW15的所有要求。” 这会导致AI产出一个大而全但可能存在诸多设计缺陷的“缝合怪”。
    *   **正确方式**:模拟我们的作业流程。
        *   “首先,请为HW13的需求设计框架,绘制类图。识别核心实体和它们的关系。”
        *   得到初步设计后,进行评审:“我认为`Library`类职责过重,能否将预约逻辑封装到单独的类中?”
        *   然后引入新需求:“现在我们增加HW14的需求(阅览室、热门书架),请在现有基础上扩展设计,并为`BookCopy`设计一个状态图。”
        *   通过这种方式,我们将一个大问题拆解成一系列小问题,AI每次只需关注一个增量,设计质量和可控性大大提高。

2.  **注入设计思想与约束 (Injecting Principles & Constraints)**:
    *   AI的设计可能大体满足要求,但很难是最好的设计,很难兼顾每一个细节。我们需要主动为其设定“护栏”。
    *   例如:“请在设计中使用**外观模式**,`Library`类作为系统的统一入口。”
    *   “`BookCopy`的行为应使用**状态模式**来管理,它的位置就是一个状态。”
    *   “`Student`和`BookCopy`之间应该是**松耦合**的,`Student`不应该直接持有`BookCopy`的对象引用,可以通过ID来关联。”
    *   通过注入这些设计原则和约束,我们能引导AI产出更健壮、更符合OO思想的架构。

3.  **扮演批判性角色 (Playing the Critic)**:
    *   AI是生成器,而人类是评审者。我们需要对AI的输出进行细致的审视和批判性提问。
    *   “这个方法的命名是否清晰地反映了它的意图?”
    *   “当两个学生同时预约最后一本书时,当前设计是否存在竞态条件?”
    *   “这个设计在未来的需求变更(例如增加电子书)面前,扩展性如何?”
    *   这种人机协作的迭代循环,能将AI的快速生成能力与人类的深度思考和经验判断相结合,最终打磨出优秀的架构。

与AI进行协作设计,我们不能完全依靠AI,架构的选择需要人类来最终确定。此外,AI生成的代码,往往会有BUG,很难兼顾每一个细节,需要人类来找出这些问题所在。

## 四、 思想演进之路

### 1. 架构设计思维的演进

*   **第一单元 (表达式求导)**:这是架构思维的启蒙。我从面向过程的思维中跳出,学会了将“表达式”、“项”、“因子”等概念抽象成对象。设计的核心是**数据抽象**和**层次化表示**(表达式包含项,项包含因子),并通过多态实现`derive()`等操作。如果不用卷性能分,这单元的体验是非常好的。卷性能分,代码的复杂度大大增加,而且分数不一定能略微提升,反而可能造成失误。

*   **第二单元 (多线程电梯)**:架构思维再一次跃迁——从静态结构到**动态协作**,从单线程到**并发系统**。设计的核心不再是数据结构,而是对象间的交互协议和线程安全。我学习并实践了**生产者-消费者模式**(调度器为生产者,电梯为消费者)、以及`synchronized`带来的线程同步问题。这个单元,我认为是收获最大的一个单元,多线程编程,和单线程编程,有很多截然不同的考虑。如何避免临界区访问冲突,如何避免死锁,这些多线程编程需要考虑的问题,拓展了思维,大大提高了编程的能力。

*   **第三单元 (JML与社交网络)**:这是一次“戴着镣铐跳舞”的经历。架构很大程度上由JML规格预先定义,我们的任务是在遵循规格的前提下,进行高效、正确的实现。设计的核心转向了**数据管理**和**算法效率**。为了顺利通过强测,我引入了缓存、并查集等数据结构和算法来优化查询性能。我个人的看法比较片面,随着电脑性能的逐步提高和大模型的逐渐普及,没有必要为了一点点的性能提升,写复杂的代码。如无必要,勿增实体。也许,OO为了课程能够拉开差距,不得不设计一些极端数据点来卡人,这能理解。但更多的时候,我们是根据实际应用场景来选择算法,而且通常不需要追求极端性能。而且个人认为,U3的一些数据点设计不是很好,把一类情况考虑得很极端,而另一类情况没有考虑,不得不以空间换时间。但是以空间换时间的做法,既不优雅,也没有现实价值,只是为了通过测试,还大大提高了工作量。

*   **第四单元 (UML与图书馆)**:回归本源,自由度与责任并存。我们再次成为架构的主导者,但这次手握UML这一强大的设计语言。设计的核心是**对现实世界业务逻辑的建模**。状态模式、外观模式等设计模式被自然而然地应用进来。这次的设计综合了前几个单元的经验:既有第一单元的数据抽象(`Student`, `BookCopy`),又有第二单元对象间的协作(但非并发),还有第三单元对复杂规则的严谨处理。架构思维至此完成了一个循环,从具体到抽象,从自由到约束,再到带着方法论的自由,变得更加成熟和全面。

### 2. 测试思维的演进

*   **第一单元**:测试相对简单,核心是**功能与边界测试**。通过Python脚本大量生成合法的、非法的、以及各种边界情况(如0、空、超长、复杂嵌套)的表达式,与标准答案(如Python的Sympy库)进行对拍。自动化测试的雏形在此建立。

*   **第二单元**:测试更为复杂。单纯的功能测试已完全不够用。既要测试功能的正确性,又要测试性能得分,还要测试有无死锁的可能。U2第一次作业,根据指导书内容,我并没有意识到死锁的测试的重要性。在自己电脑上运行时,几百次测试没有出现死锁,以为没有任何问题。我觉得写代码,抛开应用场景,是不合理的,拉开分数的差距,仅仅是通过信息差,也是不合理的。想到这里也就释怀了,OO课程拿个过得去的分数即可,没有必要在无价值的内卷上花费大量时间。

*   **第三单元**:测试的核心是**规格符合性测试**和**性能压力测试**。JML为我们提供了明确的“标准答案”,测试用例的构造可以直接针对JML的前置条件、后置条件和不变量来设计。同时也要求进行Junit测试编写。功能的正确性测试非常简单。有的作业,强测测试数据及其恶心,没有全面测试(A房简单数据能刀很多人),而且偏好空间复杂度换时间复杂度。其实使用空间复杂度换时间复杂度的算法,也存在强测范围内的合理数据能够卡住。这种数据设计的偏好,令人厌恶!

*   **第四单元**:第四单元比较简单,不需要过多的边界数据。只需要保证基本功能的正确性。

## 五、 课程收获总结

回顾四个单元,OO课程的收获主要有4个方面:

- 多线程编程

- 面向对象的思维

- 大语言模型的使用

- UML建模

为什么只有这3个方面呢,因为我觉得别的地方的收获不重要。比如JML,任何搜索引擎搜索‘JML’关键字,都是BUAA OO相关的内容。为什么别的地方不用JML呢。我认为U3的课程设计和现实实际开发脱节了。与JML相比,UML在企业实际开发中,更常使用,相对来说更有价值。

此外多线程编程和面向对象思维,这两个部分,我认为是收获最大的,因为这两个部分现实价值最大,未来工作中,可能会经常遇到。在OO课程中,我也经常使用大语言模型,逐渐掌握了更好地使用大模型方法,我认为这方面的收获有意义但不大,现在企业开发中,往往需要大模型辅助来提高效率,有一定价值,但是不多,因为大模型的迭代更新很快,很可能现在非常有用的使用大模型的一些技巧,在将来更先进的大模型面前,会显得没有必要。

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

272

社区成员

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

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