OO课程第四单元总结与全课程回顾

王新宇-24231208 2026-06-25 22:37:11

2026OO课程第四单元总结与全课程回顾:正向建模驱动下的架构演进之路

一、引言

2026年面向对象设计与构造课程已圆满落幕。回顾四个单元的学习历程,从基础的Java语法训练到复杂的系统设计,从手工编码到UML正向建模,每一阶段都是一次思维的跃迁。第四单元作为课程的收官之作,引入了正向建模与开发的全新范式,让我深刻认识到UML模型在现代软件工程中的核心价值。本文将从第四单元的实践出发,系统总结正向建模的实践经验、架构设计思维演进、测试思维蜕变以及整个课程的收获与感悟。

二、第四单元正向建模实践

2.1 正向建模与开发的核心内涵

正向建模(Forward Modeling)是指从抽象的UML模型出发,通过模型驱动的方式生成或指导代码实现的过程。与传统的"先写代码后画图"不同,正向建模强调"模型先行",要求开发者在编码之前完成系统的结构化设计,确保模型与代码之间建立严格的追踪关系。

在第四单元的三次作业中,我们分别完成了:

  • 第一次作业:基于给定的UML类图实现图书馆管理系统的基础功能
  • 第二次作业:扩展类图并实现顺序图(Sequence Diagram)的交互逻辑
  • 第三次作业:完善状态图(Statechart Diagram)并实现状态驱动的业务逻辑

2.2 两阶类图在正向建模中的关键作用

第四单元最核心的设计理念是"两阶类图"(Two-Tier Class Diagram),即先构建概念模型(Conceptual Model),再细化为设计模型(Design Model)。这种分层建模策略在正向建模过程中发挥了至关重要的作用。

2.2.1 第一阶:概念模型——领域实体的抽象

概念模型关注"系统是什么",主要提取领域中的核心实体及其关系。在图书馆管理系统中,概念模型包含:

核心实体:
- Book(图书):抽象类,定义ISBN、类型、副本列表等属性
- BookCopy(图书副本):具体实例,追踪位置、借阅状态、历史记录
- User(用户):管理学分、借阅记录、预约状态
- Library(图书馆):协调所有实体,处理业务请求
- Location(位置枚举):BS/TBS/BRO/AO/RR/USER六种位置

概念模型的价值在于:

  1. 统一领域语言:通过UML类图明确实体命名和职责边界,避免开发过程中的语义歧义
  2. 识别核心关系:Library 1→* Book、Book 1→* BookCopy、User 1→* BookCopy等关联关系在模型层面确立
  3. 指导数据结构设计:概念模型直接映射到代码中的Map/List等数据结构选型

2.2.2 第二阶:设计模型——行为与交互的细化

设计模型关注"系统怎么做",在概念模型的基础上补充方法签名、可见性、参数类型等实现细节。例如:

// 概念模型:Book可以借阅
// 设计模型:明确方法签名
public abstract boolean canBorrow();
public abstract boolean canOrder();
public abstract int getBorrowLimit();

// 概念模型:BookCopy有位置
// 设计模型:增加移动追踪
public void move(String date, Location to);
public void reserve(String date, LocalDate until, String userId);

两阶类图的优势体现在:

  1. 降低认知负荷:先理解领域概念,再关注实现细节,符合人类认知规律
  2. 支持迭代设计:概念模型稳定后,设计模型可以独立演进而不影响整体架构
  3. 提升代码质量:设计模型中的方法签名直接对应代码实现,减少"边想边写"带来的设计缺陷

2.3 架构设计与UML模型的追踪关系

2.3.1 类图追踪:从模型到代码的精准映射

在最终的代码实现中,UML类图与实际代码保持了严格的一致性:

UML元素代码实现追踪验证
Book抽象类public abstract class Book✅ 完全一致
BookCopy类属性private String id; private Location location;✅ 完全一致
User#creditScoreprivate int creditScore;✅ 完全一致
Library#booksprivate Map<String, Book> books;✅ 完全一致
canBorrow()抽象方法public abstract boolean canBorrow();✅ 完全一致

特别值得注意的是,在第三阶段迭代中,UML模型增加了overduePenalized属性和对应的getter/setter方法,代码实现也同步更新:

// uml_ultimate.mdj 中的设计模型
{"_type":"UMLAttribute","_id":"M004AA","_parent":{"$ref":"M004"},"name":"overduePenalized","visibility":"private","type":"boolean"}

// 代码实现
private boolean overduePenalized;
public boolean isOverduePenalized() { return overduePenalized; }
public void setOverduePenalized(boolean penalized) { this.overduePenalized = penalized; }

2.3.2 顺序图追踪:消息交互的精确还原

顺序图(Sequence Diagram)描述了对象之间的消息传递时序。在第二次作业中,我们设计了"预约-取书"的交互流程:

用户 -> 图书馆: orderNewBook(dateStr, uid, isbn, req)
图书馆 -> 用户: hasAppointment()
图书馆 -> 用户: setAppointIsbn(isbn)

用户 -> 图书馆: getOrderedBook(today, dateStr, uid, isbn, req)
图书馆 -> 副本: getReserveUser()
图书馆 -> 副本: move(dateStr, Location.USER)
图书馆 -> 用户: borrow(copy)
图书馆 -> 用户: clearAppointment()

代码实现中,这些消息对应具体的方法调用:

// Library.java
@SendMessage(from = "Library", to = "User")
public void orderNewBook(String dateStr, String uid, String isbn, LibraryReqCmd req) {
    doOrder(dateStr, uid, isbn, req);
}

@Trigger(from = "AppointmentOffice", to = {"User"})
@SendMessage(from = "Library", to = "User")
private void doPick(LocalDate today, String dateStr, String uid, String isbn, LibraryReqCmd req) {
    // ... 验证逻辑
    reservedCopy.move(dateStr, Location.USER);
    reservedCopy.clearReserve();
    user.borrow(reservedCopy);
    user.clearAppointment();
}

关键洞察:顺序图中的每个消息(Message)必须对应类中真实存在的方法,且消息的source和target类之间需存在关联关系。这是评测器校验的核心规则。

2.3.3 状态图追踪:状态迁移的逻辑验证

状态图(Statechart Diagram)是第三阶段的核心交付物,描述了BookCopy在其生命周期中的状态变化:

状态集合:
- Bookshelf (BS)
- TreasuredBookshelf (TBS)
- ReadingRoom (RR)
- BorrowReturnOffice (BRO)
- AppointmentOffice (AO)
- User

迁移规则示例:
Bookshelf -> User: doBorrow() [guard: creditScore > 80]
User -> BorrowReturnOffice: doReturn()
Bookshelf -> ReadingRoom: doRead() [guard: creditScore >= 60]
ReadingRoom -> BorrowReturnOffice: doRestore()

代码实现中,状态迁移通过Location枚举和move()方法实现:

// BookCopy.java
public void move(String date, Location to) {
    Location from = this.location;
    this.location = to;
    traces.add(new Trace(date, from, to));  // 记录状态迁移历史
}

状态图必须满足的约束(根据评测规则):

  1. 有且仅有一个无名起始状态(仅外出迁移)
  2. 0或1个终止状态(仅进入迁移)
  3. 任意状态S必须存在从起始状态到S的迁移路径
  4. 若存在终止状态,则从S到终止状态也必须存在路径

代码实现通过Location枚举的六个值和move()方法的调用确保所有状态可达。

三、大模型辅助正向建模的实践与思考

3.1 大模型在架构设计中的定位

在第四单元的实践中,我尝试使用大语言模型(LLM)辅助完成UML建模和架构设计任务。通过多次迭代,我总结出大模型在复杂场景中的三种角色:

3.1.1 概念提取器:从需求到实体识别

提示词示例

请分析以下图书馆管理系统需求,提取核心领域实体及其属性:
- 图书分为A/B/C三种类型,各有不同的借阅规则
- 图书副本可以在书架、阅览室、借还处、预约处、用户手中流转
- 用户有学分系统,学分影响借阅权限

大模型输出

  • Book(类型、ISBN、副本列表)
  • BookCopy(位置、预约状态、借阅截止日期)
  • User(学分、借阅记录、预约状态)
  • Library(图书集合、用户集合)

人工验证:实体识别准确,但需要补充Rating(评分)和Trace(迁移记录)两个辅助实体。

3.1.2 关系推断器:从实体到关联设计

提示词示例

已知以下实体,请推断它们之间的关联关系及多重性:
- Library
- Book (isbn, type)
- BookCopy (id, location)
- User (id, creditScore)

大模型输出

  • Library 1 → * Book(图书馆管理多本图书)
  • Book 1 → * BookCopy(一本图书有多个副本)
  • User * → * BookCopy(用户可借阅多本副本,副本可被多用户借阅过)

人工验证:关联关系正确,但需注意User与BookCopy的实际关联是"当前借阅"(1→*),而非历史借阅。

3.1.3 代码生成器:从模型到实现 skeleton

提示词示例

根据以下UML类图设计,生成Java代码骨架:
class BookCopy {
  -id: String
  -location: Location
  -traces: List<Trace>
  +move(date, to)
  +reserve(date, until, userId)
}

大模型输出

public class BookCopy {
    private String id;
    private Location location;
    private List<Trace> traces;
    
    public BookCopy(String id) {
        this.id = id;
        this.location = Location.BS;
        this.traces = new ArrayList<>();
    }
    
    public void move(String date, Location to) {
        // TODO: 实现位置移动逻辑
    }
    
    public void reserve(String date, LocalDate until, String userId) {
        // TODO: 实现预约逻辑
    }
}

人工验证:代码骨架结构正确,但需补充业务逻辑细节(如traces记录、状态校验等)。

3.2 引导大模型的关键技巧

通过实践,我总结出以下引导大模型完成架构设计任务的经验:

3.2.1 分层提示:从抽象到具体

不要一次性抛出完整需求,而是按照"概念模型→设计模型→代码实现"的顺序逐步引导:

第一轮:请识别领域实体
第二轮:请定义实体间的关联关系
第三轮:请为每个类设计方法签名
第四轮:请生成代码骨架
第五轮:请补充业务逻辑

3.2.2 约束明确:提供评测规则

在提示词中明确说明评测器的校验规则,避免大模型生成不符合规范的设计:

注意:
- UML顺序图中,lifeline必须对应具体实现的类(不能是抽象类)
- message必须与类中真实存在的方法名完全一致
- 状态图必须有且仅有一个无名起始状态
- 任意状态必须从起始状态可达

3.2.3 交叉验证:人工审核关键点

大模型擅长模式匹配,但不擅长业务逻辑推理。以下关键点必须人工验证:

  1. 业务规则正确性:如学分阈值(>80可借阅、>40可读A类书)
  2. 边界条件处理:如预约过期、逾期罚款的时机
  3. 状态图可达性:确保所有状态都能从起始状态到达
  4. 方法签名一致性:UML模型与代码实现的方法名、参数类型必须完全匹配

3.3 大模型的局限性

在实践中,我也意识到大模型的局限性:

  1. 缺乏业务上下文理解:大模型可以生成结构正确的UML,但无法理解"为什么预约保留5天"这样的业务决策
  2. 容易产生幻觉:在复杂交互场景中,大模型可能生成不存在的方法调用
  3. 难以处理迭代变更:当需求变更时,大模型容易遗漏旧设计的清理工作

结论:大模型是强大的辅助工具,但不能替代开发者的架构思维。最佳实践是"大模型生成骨架,人工填充灵魂"。

四、四个单元架构设计思维的演进

4.1 第一单元:面向过程的结构化思维

任务特征:表达式解析、多项式求导
设计模式:过程式编程 + 简单的类封装
思维特点

  • 关注"如何实现功能"
  • 代码组织以函数为中心
  • 数据结构与算法分离

典型代码结构

public class Main {
    public static void main(String[] args) {
        String input = sc.nextLine();
        Node expr = parse(input);
        Node derivative = derive(expr);
        System.out.println(derivative.toString());
    }
}

局限性:随着需求复杂度增加(如加入三角函数、指数函数),代码逐渐演变为"面条代码",难以维护。

4.2 第二单元:面向对象的继承与多态

任务特征:电梯调度系统
设计模式:策略模式 + 生产者-消费者模型
思维特点

  • 关注"如何组织类"
  • 使用继承实现代码复用
  • 使用多态实现行为扩展

典型设计

abstract class Elevator {
    abstract void processRequest(Request req);
}

class ElevatorA extends Elevator {
    void processRequest(Request req) {
        // A型电梯的处理逻辑
    }
}

class ElevatorB extends Elevator {
    void processRequest(Request req) {
        // B型电梯的处理逻辑
    }
}

进步:通过多态消除了大量的if-else分支,新增电梯类型只需添加新类而不修改旧代码(开闭原则)。

4.3 第三单元:设计模式的系统化应用

任务特征:社交网络分析与JML规格验证
设计模式:工厂模式、观察者模式、装饰器模式
思维特点

  • 关注"如何解耦组件"
  • 使用设计模式解决常见问题
  • 重视接口的抽象设计

典型应用

// 工厂模式:创建不同类型的Network
interface NetworkFactory {
    Network createNetwork();
}

// 观察者模式:消息通知机制
interface Observer {
    void update(Message msg);
}

class User implements Observer {
    void update(Message msg) {
        // 处理新消息
    }
}

突破:开始从"实现功能"转向"设计架构",意识到好的设计可以让代码自然生长。

4.4 第四单元:模型驱动的正向设计

任务特征:图书馆管理系统 + UML正向建模
设计模式:状态模式 + 命令模式 + 模型驱动架构
思维特点

  • 关注"系统应该是什么"
  • 先建模后编码,模型即设计
  • 强调模型与代码的追踪关系

典型工作流

需求分析 → 概念模型 → 设计模型 → 代码骨架 → 业务逻辑 → 测试验证
   ↓           ↓           ↓          ↓          ↓          ↓
 领域实体    UML类图1    UML类图2    Java类    方法实现    单元测试

质变:从"边写边想"到"先想后写",从"代码即设计"到"模型即设计"。UML不再是事后补画的文档,而是指导开发的蓝图。

4.5 演进总结

单元核心思维设计重心质量指标
第一单元面向过程算法正确性功能是否实现
第二单元面向对象类的设计代码是否复用
第三单元设计模式组件解耦架构是否灵活
第四单元模型驱动系统设计模型是否准确

思维跃迁轨迹

如何实现(How) → 如何组织(Structure) → 如何解耦(Decouple) → 应该是什么(What)

五、四个单元测试思维的演进

5.1 第一单元:暴力测试——"能跑就行"

测试策略:手动输入几个样例,看输出是否正确
测试用例

  • 正常情况:x + 2*x3
  • 边界情况:00
  • 特殊情况:x*x*x3*x*x

问题

  • 测试覆盖率极低
  • 无法处理大规模随机测试
  • 回归测试完全靠人工

5.2 第二单元:自动化测试——"脚本辅助"

测试策略:编写Python脚本生成测试用例,对比输出
测试工具

import subprocess
import random

def generate_test():
    # 随机生成电梯请求序列
    requests = []
    for _ in range(random.randint(10, 50)):
        requests.append(f"[{random.randint(1,100)}]FROM-{random.randint(1,10)}-TO-{random.randint(1,10)}")
    return "\n".join(requests)

# 运行程序并对比输出
result = subprocess.run(["java", "-jar", "elevator.jar"], input=generate_test(), capture_output=True, text=True)

进步

  • 测试覆盖率提升
  • 支持大规模随机测试
  • 可重复执行

局限

  • 脚本逻辑复杂度高
  • 难以验证中间状态(如电梯内部请求队列)

5.3 第三单元:规格测试——"契约验证"

测试策略:基于JML规格编写测试用例,验证方法是否符合契约
测试方法

// JML规格
/*@ requires \contains(user1) && \contains(user2);
  @ ensures \result == (\sum int i; isLinked(user1, i) && isLinked(user2, i); 1);
  @*/
public int queryCircleCount(int id1, int id2);

// 测试用例
@Test
public void testQueryCircleCount() {
    network.addPerson(person1);
    network.addPerson(person2);
    network.addRelation(person1, person3);
    network.addRelation(person2, person3);
    assertEquals(1, network.queryCircleCount(person1.getId(), person2.getId()));
}

突破

  • 测试从"黑盒"转向"白盒"
  • 基于规格验证,而非基于样例猜测
  • 支持单元测试框架(JUnit)

5.4 第四单元:模型测试——"一致性校验"

测试策略:验证UML模型与代码实现的一致性,确保模型驱动的正确性
测试维度

5.4.1 结构一致性测试

  • 类图中的类、属性、方法是否在代码中存在
  • 关联关系的多重性是否正确实现
  • 继承关系是否准确映射

5.4.2 行为一致性测试

  • 顺序图中的消息是否对应真实方法调用
  • 状态图中的迁移是否在代码中实现
  • 守卫条件(guard)是否转化为if判断

5.4.3 约束一致性测试

  • UML模型的可见性(public/private/protected)是否与代码一致
  • 抽象方法是否在子类中实现
  • 枚举值是否完整覆盖

测试工具链

UML模型 (.mdj)
    ↓
模型解析器
    ↓
一致性校验器 ← → 代码解析器 (JavaParser)
    ↓
测试报告

质变:测试从"验证代码是否正确"升级为"验证模型与代码是否一致",测试的重心从实现细节转向设计意图。

5.5 测试思维总结

单元测试理念测试方法验证目标
第一单元功能验证手动测试输出是否正确
第二单元自动化验证脚本对比大规模用例覆盖
第三单元契约验证JML规格测试方法是否符合契约
第四单元模型验证一致性校验模型与代码是否一致

测试思维跃迁

黑盒测试 → 白盒测试 → 规格测试 → 模型测试
  ↓           ↓           ↓           ↓
样例验证   代码覆盖   契约满足   设计一致

六、课程收获与反思

6.1 技术能力的提升

6.1.1 Java编程能力

  • 熟练掌握面向对象编程的核心概念(封装、继承、多态)
  • 能够运用设计模式解决常见架构问题
  • 理解泛型、反射、注解等高级特性

6.1.2 UML建模能力

  • 能够使用类图、顺序图、状态图表达系统设计
  • 理解正向建模的工作流与价值
  • 掌握模型与代码的追踪方法

6.1.3 测试工程能力

  • 从手工测试到自动化测试的工程化思维
  • 基于规格的测试用例设计方法
  • 模型驱动的一致性校验技术

6.2 架构思维的蜕变

6.2.1 从"实现者"到"设计者"

课程初期,我关注的是"如何让代码跑起来";课程后期,我思考的是"如何让设计更优雅"。这种转变不仅体现在代码质量上,更体现在解决问题的思维方式上。

6.2.2 从"局部优化"到"全局视角"

第一单元时,我会为了一个算法的几毫秒优化绞尽脑汁;第四单元时,我会先思考整体架构是否合理,再关注局部实现。架构思维让我学会了"先做对,再做好"。

6.2.3 从"经验驱动"到"模型驱动"

过去的开发依赖个人经验,容易陷入"试错-修改"的循环;现在的开发基于UML模型,能够在编码之前发现设计缺陷,大幅降低返工成本。

6.3 工程实践的感悟

6.3.1 好的设计是演进而来的,不是规划出来的

四个单元的作业难度递增,架构也在不断重构。我深刻认识到,没有一蹴而就的完美架构,只有在迭代中不断演进的合理架构。关键是保持架构的灵活性,让系统能够"自然生长"。

6.3.2 测试不是负担,而是安全网

从第一单元的"懒得写测试"到第四单元的"先写测试再写代码",我意识到测试不是开发的负担,而是保障代码质量的必要手段。好的测试让你敢于重构,敢于变更。

6.3.3 工具是手段,思维是核心

大模型、UML工具、测试框架都是辅助手段,真正决定代码质量的是开发者的架构思维。工具可以提升效率,但不能替代思考。

6.4 不足与展望

6.4.1 仍需提升的方面

  1. 性能优化意识:在复杂场景下,对时间复杂度和空间复杂度的敏感度仍需加强
  2. 并发编程能力:课程作业以单线程为主,对多线程、锁机制的实践经验不足
  3. 团队协作经验:个人作业无法模拟真实团队协作中的代码合并、冲突解决等场景

6.4.2 未来学习方向

  1. 深入设计模式:系统学习GoF 23种设计模式的适用场景与实现细节
  2. 掌握领域驱动设计(DDD):将第四单元的正向建模思想延伸到更复杂的业务场景
  3. 学习微服务架构:从单体应用走向分布式系统,理解服务拆分、API设计、数据一致性等挑战

七、结语

2026OO课程是我大学期间最具挑战也最具收获的课程之一。从第一单元的手忙脚乱到第四单元的从容应对,从面向过程到模型驱动,每一次重构都是思维的蜕变,每一次调试都是能力的提升。

特别感谢老师的悉心指导和助教的耐心答疑。课程的结束不是终点,而是新的起点。面向对象的思想、架构设计的思维、测试驱动的理念,将伴随我未来的整个职业生涯。

最后,用一句话总结这门课程带给我的改变

"代码是写给人看的,只是恰好能在机器上运行。" —— 好的架构让代码自解释,好的设计让系统自演进。


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

309

社区成员

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

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