1.MIT Software Construction:软件工程核心知识提炼


目录

  • [1. 为什么学习 Software Construction](#1. 为什么学习 Software Construction)
  • [2. 软件工程最重要的三个目标](#2. 软件工程最重要的三个目标)
    • [2.1 Safe from Bugs](#2.1 Safe from Bugs)
    • [2.2 Easy to Understand](#2.2 Easy to Understand)
    • [2.3 Ready for Change](#2.3 Ready for Change)
  • [3. Specification:先定义"软件应该做什么"](#3. Specification:先定义“软件应该做什么”)
    • [3.1 一个好的功能应该明确](#3.1 一个好的功能应该明确)
  • [4. Abstraction:抽象](#4. Abstraction:抽象)
  • [5. Interface:接口](#5. Interface:接口)
    • [5.1 接口解决什么问题?](#5.1 接口解决什么问题?)
  • [6. ADT:抽象数据类型](#6. ADT:抽象数据类型)
  • [7. Modularity:模块化](#7. Modularity:模块化)
  • [8. Cohesion:内聚](#8. Cohesion:内聚)
  • [9. Coupling:耦合](#9. Coupling:耦合)
  • [10. 高内聚 + 低耦合](#10. 高内聚 + 低耦合)
  • [11. Immutability:不可变性](#11. Immutability:不可变性)
  • [12. Defensive Programming:防御式编程](#12. Defensive Programming:防御式编程)
  • [13. Testing:测试](#13. Testing:测试)
  • [14. Unit Test:单元测试](#14. Unit Test:单元测试)
  • [15. Regression Test:回归测试](#15. Regression Test:回归测试)
  • [16. Code Review](#16. Code Review)
  • [17. Git / Version Control](#17. Git / Version Control)
  • [18. Debugging:Debug 的正确思维](#18. Debugging:Debug 的正确思维)
  • [19. Concurrency:并发](#19. Concurrency:并发)
  • [20. Event-driven:事件驱动](#20. Event-driven:事件驱动)
  • [21. 状态设计](#21. 状态设计)
  • [22. Error Handling:错误处理](#22. Error Handling:错误处理)
  • [23. API / Module Boundary](#23. API / Module Boundary)
  • [24. 设计的一个核心问题:变化在哪里?](#24. 设计的一个核心问题:变化在哪里?)
  • [25. 一个非常重要的工程原则:封装变化](#25. 一个非常重要的工程原则:封装变化)
  • [26. AI Coding 应该如何使用这些思想?](#26. AI Coding 应该如何使用这些思想?)
  • [27. AI 时代最重要的工作流](#27. AI 时代最重要的工作流)
  • [28. 如何把 MIT Software Construction 应用到你的 GIS 项目](#28. 如何把 MIT Software Construction 应用到你的 GIS 项目)
  • [29. 第一阶段学习清单](#29. 第一阶段学习清单)
  • [30. 与后续四个资源的关系](#30. 与后续四个资源的关系)
  • [31. 最终应该掌握的核心思想](#31. 最终应该掌握的核心思想)
  • [32. 最终目标](#32. 最终目标)

1. 为什么学习 Software Construction

AI 让"写出代码"越来越容易,但真正困难的问题逐渐变成:

  • 需求应该如何拆分?
  • 一个模块应该负责什么?
  • 模块之间应该如何通信?
  • 如何降低耦合?
  • 如何让代码容易修改?
  • 如何避免修改一个功能导致其他功能崩溃?
  • 如何证明代码是正确的?
  • 如何让别人能够快速理解代码?
  • 如何让 AI 在大型项目中稳定地修改代码?

因此:

Software Construction 的核心不是某一种编程语言,而是如何把代码构建成可靠、可理解、可维护、可演进的软件。


2. 软件工程最重要的三个目标

MIT Software Construction 中非常值得建立的三个目标是:

text 复制代码
                    Software Quality
                         │
        ┌────────────────┼────────────────┐
        ↓                ↓                ↓
    Safe from bugs   Easy to understand   Ready for change
       少 Bug            易理解             易修改

也可以理解为:

2.1 Safe from Bugs

软件应该尽可能:

  • 正确
  • 可验证
  • 异常情况可控
  • 修改后不容易引入新的 Bug

2.2 Easy to Understand

代码应该:

  • 命名清晰
  • 结构清晰
  • 职责明确
  • 模块边界明确
  • 尽量减少隐式行为

因为:

代码最重要的读者通常不是编译器,而是未来的自己和其他开发者。

AI Coding 时代这一点更加重要。

如果代码结构混乱,AI 也更容易:

  • 理解错误
  • 修改错误位置
  • 产生重复代码
  • 破坏原有逻辑

2.3 Ready for Change

软件需求一定会变化。

因此应该问:

"如果三个月后需求发生变化,我需要修改多少地方?"

优秀的设计应该让:

text 复制代码
需求变化
   ↓
局部修改
   ↓
其他模块基本不受影响

而不是:

text 复制代码
需求变化
   ↓
修改 A
   ↓
A 影响 B
   ↓
B 影响 C
   ↓
整个项目开始出现 Bug

3. Specification:先定义"软件应该做什么"

Software Construction 很重要的一部分是 Specification。

可以把它理解为:

在写代码之前,明确程序应该满足什么条件。

很多开发者的问题是:

text 复制代码
需求
 ↓
直接写代码

更好的方式是:

text 复制代码
需求
 ↓
定义行为
 ↓
定义输入
 ↓
定义输出
 ↓
定义异常情况
 ↓
再开始实现

3.1 一个好的功能应该明确

例如:

加载一个 3D 模型。

不要只描述:

"把模型加载进 Cesium。"

而应该进一步定义:

输入

  • 模型路径
  • 模型类型
  • 坐标信息

输出

  • 加载成功
  • 加载失败
  • 加载进度

异常

  • 文件不存在
  • 文件格式错误
  • 坐标系不支持
  • 模型损坏
  • 用户取消加载

状态

text 复制代码
Idle
 ↓
Loading
 ↓
Success

或

Loading
 ↓
Error

或

Loading
 ↓
Cancelled

这一步实际上已经开始进入:

产品设计 + 软件设计。


4. Abstraction:抽象

软件工程中最核心的能力之一:

不要让代码过度关注具体实现,而应该关注"它提供什么能力"。

例如模型加载。

不好的方式:

text 复制代码
业务代码
 ↓
if OSGB
    ...
else if 3DTiles
    ...
else if GLTF
    ...

随着格式增加,代码越来越复杂。

更好的抽象:

text 复制代码
ModelLoader
 ├── OSGBLoader
 ├── Tiles3DLoader
 ├── GLTFLoader
 └── ...

业务代码只需要知道:

text 复制代码
loader.load()

而不需要知道具体怎么加载。


5. Interface:接口

接口的作用之一:

明确模块之间的契约。

例如:

text 复制代码
ModelLoader

load(path)
cancel()
getProgress()
dispose()

具体实现:

text 复制代码
OSGBLoader
Tiles3DLoader
GLTFLoader

只要它们遵守同一个接口,上层业务就不需要关心具体实现。


5.1 接口解决什么问题?

主要解决:

text 复制代码
业务逻辑
   ↓
具体实现

之间过度绑定的问题。

理想状态:

text 复制代码
业务逻辑
   ↓
抽象接口
   ↓
具体实现

这样以后更换底层实现时,上层代码可以尽量不动。


6. ADT:抽象数据类型

Abstract Data Type 可以理解为:

把数据和操作这些数据的行为封装起来,只暴露必要的能力。

例如一个项目管理器:

text 复制代码
ProjectManager

openProject()
closeProject()
saveProject()
getProjectInfo()

调用方不应该直接操作:

text 复制代码
内部数据结构
内部缓存
内部文件格式
内部状态

而应该通过:

text 复制代码
ProjectManager

提供的接口访问。

这实际上是在建立:

封装 + 边界。


7. Modularity:模块化

一个大型软件不能所有代码放在一起。

应该拆成多个具有明确职责的模块。

例如一个 Electron + GIS 项目:

text 复制代码
Application
│
├── UI
│
├── Project
│
├── Model
│
├── GIS
│
├── File
│
├── Config
│
└── Communication

进一步:

text 复制代码
Model
│
├── ModelService
├── ModelLoader
├── ModelRegistry
└── ModelCache

8. Cohesion:内聚

内聚关注:

一个模块内部的东西是不是都属于同一类职责。

高内聚:

text 复制代码
ModelService
 ├── loadModel()
 ├── removeModel()
 ├── getModel()
 └── updateModel()

低内聚:

text 复制代码
ModelService
 ├── loadModel()
 ├── login()
 ├── saveDatabase()
 ├── changeTheme()
 └── sendEmail()

一般应该追求:

一个模块内部的功能应该高度相关。


9. Coupling:耦合

耦合关注:

模块之间依赖得有多紧。

例如:

text 复制代码
UI
 ↓
ModelService
 ↓
Cesium API
 ↓
File System
 ↓
Database

如果每一层都直接依赖其他层,修改起来会非常困难。

更好的结构:

text 复制代码
UI
 ↓
Application Service
 ↓
Domain / Interface
 ↓
Infrastructure

核心思想:

减少模块之间不必要的直接依赖。


10. 高内聚 + 低耦合

这是软件设计中最值得长期记住的一组原则:

text 复制代码
高内聚
 +
低耦合
 =
更容易维护的软件

可以用一个问题判断:

"如果我修改这个模块,会不会被迫修改很多其他模块?"

如果经常出现:

"改这里必须改那里。"

通常说明模块边界或者依赖关系存在问题。


11. Immutability:不可变性

不可变思想的核心:

一个数据创建以后,尽量不要到处修改它。

例如:

text 复制代码
state
 ↓
多个模块共同修改
 ↓
不知道是谁改的
 ↓
Bug

不可变思维:

text 复制代码
oldState
   ↓
newState

这样状态变化更加明确。

它对于:

  • 前端状态管理
  • 并发
  • 多模块协作
  • Debug

都非常有帮助。


12. Defensive Programming:防御式编程

不要假设:

"用户一定会按照我想的方式操作。"

需要考虑:

text 复制代码
正常输入
异常输入
空值
错误格式
超大数据
重复操作
取消操作
网络失败
文件不存在
权限不足

例如:

text 复制代码
loadModel(path)

不能只考虑:

text 复制代码
path 正确
 ↓
加载

还要考虑:

text 复制代码
path == null
文件不存在
格式错误
文件损坏
文件过大
加载超时
用户取消
重复加载

13. Testing:测试

测试不是为了"证明代码完全没有 Bug"。

测试更重要的作用是:

建立对代码行为的信心。

尤其是 AI Coding。

AI 修改代码之后,如果没有测试:

text 复制代码
AI 修改
 ↓
看起来能运行
 ↓
不知道有没有破坏其他功能

有测试:

text 复制代码
AI 修改
 ↓
运行测试
 ↓
通过
 ↓
信心增加

14. Unit Test:单元测试

一个模块应该能够被独立测试。

例如:

text 复制代码
CoordinateService

可以测试:

text 复制代码
WGS84 → WebMercator
WGS84 → GCJ02
Invalid Coordinate
Null Input

而不需要启动整个 GIS 系统。

这说明:

好的模块通常也是容易测试的模块。


15. Regression Test:回归测试

回归测试解决的问题:

新代码有没有破坏旧功能?

例如:

text 复制代码
原来:
模型加载 ✓

增加:
3D Tiles ✓

但是:
OSGB ✗

如果有自动测试:

text 复制代码
修改代码
 ↓
测试
 ↓
发现 OSGB 测试失败
 ↓
立即定位问题

这对 AI Coding 非常重要。


16. Code Review

Code Review 不只是检查:

"有没有语法错误?"

更应该检查:

结构

  • 模块职责是否清晰?
  • 有没有高耦合?
  • 有没有重复代码?

设计

  • 抽象是否合理?
  • 是否过度设计?
  • 接口是否稳定?

可靠性

  • 异常是否处理?
  • 资源是否释放?
  • 并发是否安全?

可维护性

  • 三个月以后还能看懂吗?
  • 需求变化后容易修改吗?

17. Git / Version Control

版本控制不仅是:

text 复制代码
git add
git commit
git push

更重要的是:

让软件开发过程可以被安全地回退和比较。

AI Coding 尤其应该重视 Git。

推荐:

text 复制代码
一个功能
 ↓
一个 Branch
 ↓
AI 修改
 ↓
查看 Diff
 ↓
测试
 ↓
Commit

不要让 AI 一次修改几百个文件以后才开始看:

"到底发生了什么?"


18. Debugging:Debug 的正确思维

不要:

text 复制代码
看到 Bug
 ↓
直接让 AI 改

应该:

text 复制代码
Bug
 ↓
复现
 ↓
定位
 ↓
建立假设
 ↓
验证假设
 ↓
找到根因
 ↓
修复
 ↓
测试

特别是 AI Debug 时,要让 AI:

先解释 Bug 的根因,再修改代码。

例如:

text 复制代码
请先分析这个 Bug 的根本原因。

不要修改代码。

请告诉我:
1. Bug 如何产生
2. 涉及哪些模块
3. 哪个状态发生了异常
4. 为什么现有代码没有阻止它
5. 有哪些修复方案

19. Concurrency:并发

虽然目前不一定马上用到,但这是工程能力的重要组成部分。

典型问题:

text 复制代码
用户点击加载
 ↓
任务 A 开始

用户再次点击
 ↓
任务 B 开始

A、B 同时修改同一个状态
 ↓
状态混乱

需要考虑:

  • Race Condition
  • Thread Safety
  • Shared State
  • Lock
  • Atomic Operation
  • Cancellation

在 Electron + ZMQ + GIS + AI 的项目中,这类问题会越来越常见。


20. Event-driven:事件驱动

很多复杂应用可以通过事件解耦。

例如:

text 复制代码
ModelService
    ↓
model:loading

ModelService
    ↓
model:progress

ModelService
    ↓
model:loaded

ModelService
    ↓
model:error

其他模块只订阅自己需要的事件:

text 复制代码
UI
 ↓
监听 model:progress

Logger
 ↓
监听 model:error

SceneManager
 ↓
监听 model:loaded

这样模块之间不需要全部直接调用。


21. 状态设计

复杂软件最容易出 Bug 的地方之一就是:

状态。

例如模型:

text 复制代码
Idle
Loading
Loaded
Failed
Cancelled
Disposing
Disposed

不要让代码中到处出现:

text 复制代码
isLoading
isLoaded
isError
isCancelled

然后互相组合产生几十种未知状态。

应该明确:

text 复制代码
ModelState

并定义合法状态转换:

text 复制代码
Idle
 ↓
Loading
 ├── Loaded
 ├── Failed
 └── Cancelled

这会显著降低复杂度。


22. Error Handling:错误处理

错误不是简单的:

text 复制代码
try {
    ...
} catch {
    console.log(error)
}

应该思考:

错误在哪里产生?

text 复制代码
File System
 ↓
Model Loader
 ↓
Service
 ↓
UI

谁负责处理?

例如:

text 复制代码
底层
 ↓
产生技术错误

Service
 ↓
转换成业务错误

UI
 ↓
展示用户能够理解的信息

例如:

text 复制代码
ENOENT

用户不一定需要看到。

可以转换为:

text 复制代码
模型文件不存在,请重新选择文件。

23. API / Module Boundary

一个模块应该明确:

text 复制代码
输入什么
输出什么
可以调用什么
不能调用什么

例如:

text 复制代码
ModelService

允许:
load()
remove()
get()

不允许:
直接修改 UI DOM
直接修改数据库
直接操作 Renderer 状态

这就是:

边界意识。

架构能力本质上很大一部分就是:

建立边界。


24. 设计的一个核心问题:变化在哪里?

这是非常值得培养的思维。

每次设计一个模块,都问:

"未来什么地方最可能发生变化?"

例如:

text 复制代码
模型格式

可能变化:

text 复制代码
OSGB
3D Tiles
GLTF
未来的新格式

因此应该把:

text 复制代码
模型格式相关代码

隔离起来。

又例如:

text 复制代码
GIS 引擎

未来可能从:

text 复制代码
Cesium

换成:

text 复制代码
其他 GIS Engine

那么就应该尽量避免整个业务层直接依赖 Cesium。


25. 一个非常重要的工程原则:封装变化

可以记住:

把容易变化的东西封装起来。

例如:

text 复制代码
                业务逻辑
                   │
                   ↓
             ModelLoader
                   │
          ┌────────┼────────┐
          ↓        ↓        ↓
        OSGB     3DTiles   GLTF

这样未来增加:

text 复制代码
OBJ

主要增加:

text 复制代码
OBJLoader

而不是修改整个业务系统。


26. AI Coding 应该如何使用这些思想?

以后不要只问:

text 复制代码
"帮我实现 XXX。"

可以逐渐变成:

需求分析

text 复制代码
请先分析这个需求涉及哪些模块。
不要写代码。

架构分析

text 复制代码
请分析当前模块之间的依赖关系,
指出高耦合和职责混乱的位置。
不要修改代码。

方案设计

text 复制代码
请给出 2~3 个设计方案,
分析各自的优缺点。
暂时不要实现。

实现

text 复制代码
按照确定的方案实现。

要求:
1. 保持现有功能
2. 尽量减少修改范围
3. 每完成一个阶段运行测试
4. 不要引入不必要的抽象

Review

text 复制代码
请 Review 刚才的修改。

重点检查:
1. Bug 风险
2. 高耦合
3. 低内聚
4. 重复代码
5. 状态问题
6. 异常处理
7. 资源泄漏
8. 可测试性
9. 可维护性
10. 是否过度设计

27. AI 时代最重要的工作流

最终建议形成:

text 复制代码
                需求
                 ↓
             Specification
                 ↓
             模块划分
                 ↓
            接口 / 边界
                 ↓
             方案设计
                 ↓
             AI 实现
                 ↓
               Test
                 ↓
             Code Review
                 ↓
              Refactor
                 ↓
              Commit

而不是:

text 复制代码
需求
 ↓
AI 写代码
 ↓
Bug
 ↓
AI 修
 ↓
Bug
 ↓
AI 再修
 ↓
代码越来越乱

28. 如何把 MIT Software Construction 应用到你的 GIS 项目

以:

"实现本地 3D 模型加载"

为例。

第一层:Specification

定义:

text 复制代码
输入:
模型路径 + 模型类型

输出:
加载成功 / 失败 / 进度

异常:
文件不存在 / 格式错误 / 坐标错误 / 用户取消

第二层:模块

text 复制代码
UI
 ↓
ModelService
 ↓
ModelLoader
 ↓
FileSystem

第三层:抽象

text 复制代码
ModelLoader
 ├── OSGBLoader
 ├── Tiles3DLoader
 └── GLTFLoader

第四层:状态

text 复制代码
Idle
 ↓
Loading
 ├── Success
 ├── Failed
 └── Cancelled

第五层:测试

text 复制代码
正常模型
错误路径
错误格式
超大模型
重复加载
取消加载
加载失败

第六层:Review

检查:

text 复制代码
是否高耦合?
是否职责混乱?
是否存在重复代码?
是否容易增加新的模型格式?
是否容易测试?
是否存在资源泄漏?

这整个过程,就是 Software Construction 的思想在实际项目中的落地。


29. 第一阶段学习清单

不需要 Java,也可以按照下面的顺序学习。

Level 1:软件质量

  • Safe from Bugs
  • Easy to Understand
  • Ready for Change

Level 2:Specification

  • 明确输入
  • 明确输出
  • 明确状态
  • 明确异常
  • 明确边界

Level 3:模块化

  • Module
  • Cohesion
  • Coupling
  • Interface
  • Abstraction
  • Encapsulation

Level 4:可靠性

  • Defensive Programming
  • Error Handling
  • State Management
  • Resource Management

Level 5:测试

  • Unit Test
  • Integration Test
  • Regression Test
  • Testable Design

Level 6:工程实践

  • Git
  • Code Review
  • Debugging
  • Refactoring

Level 7:复杂系统基础

  • Concurrency
  • Event-driven
  • Thread Safety
  • Networking

30. 与后续四个资源的关系

MIT Software Construction 不需要单独学完所有"架构"。

它更像是整个学习路线的地基:

text 复制代码
MIT Software Construction
        │
        │ 软件工程基本功
        ↓
     Refactoring
        │
        │ 代码如何持续变好
        ↓
   Design Patterns
        │
        │ 如何组织复杂代码
        ↓
 Clean Architecture
        │
        │ 如何建立系统边界
        ↓
       DDIA
        │
        │ 如何设计复杂系统
        ↓
   System Design

所以:

MIT Software Construction 的任务不是教你成为架构师,而是建立"工程师思维"。


31. 最终应该掌握的核心思想

如果最后只保留十条,请记住:

1. 先定义问题,再写代码

text 复制代码
Specification → Implementation

2. 一个模块应该有清晰的职责

text 复制代码
高内聚

3. 模块之间尽量少互相依赖

text 复制代码
低耦合

4. 用接口隔离具体实现

text 复制代码
业务 → Interface → Implementation

5. 把变化隔离起来

text 复制代码
容易变化的东西 → 独立封装

6. 让代码容易测试

text 复制代码
好的设计 → 更容易测试

7. 不要害怕重构

text 复制代码
Working Code ≠ Good Code

8. 用测试保护重构

text 复制代码
Refactor
 ↓
Test
 ↓
Confidence

9. 不要为了架构而架构

text 复制代码
复杂度来自需求
而不是来自设计者

10. AI 是施工队,你是设计者

text 复制代码
你:
需求 / 判断 / 设计 / 架构

AI:
实现 / 测试 / Debug / Review / 重构

32. 最终目标

学习 MIT Software Construction 最终不是为了:

"掌握 Java。"

也不是为了:

"把课程作业做完。"

真正应该获得的是:

一种构建软件的思维方式。

以后面对一个功能时,你会自然开始思考:

text 复制代码
这个功能的边界是什么?
        ↓
谁负责?
        ↓
依赖谁?
        ↓
谁依赖它?
        ↓
什么东西可能变化?
        ↓
状态有哪些?
        ↓
异常怎么办?
        ↓
怎么测试?
        ↓
以后怎么扩展?

当这些问题逐渐变成你的习惯之后,你的 AI Coding 能力也会发生本质变化。

你不再只是:

"让 AI 写代码。"

而会变成:

"我负责设计软件,AI 帮我高效率完成软件构建。"

这才是 AI 编程时代真正值得培养的工程能力。

相关推荐
XM_jhxx3 小时前
简会AI图纸识别系统功能上新:模板、公差、量具个性化配置上线!
数据库·人工智能·软件工程
rolt6 小时前
OGC CityGML-用UML表示的行业标准03地理
软件工程·uml·ontology·本体
传奇开心果编程19 小时前
中庸、有容、破执:仓颉编程语言的中华智慧结晶——三重哲学境界思辨
开发语言·华为·开源·软件工程
梁辰兴1 天前
软件工程:面向功能的度量
软件工程·梁辰兴·面向功能的度量·度量定义·功能点分析fpa·用例点ucp·生产率分析
GISMagic1 天前
AI 时代的软件工程与架构能力提升路线
人工智能·架构·软件工程
沐欣工作室_lvyiyi1 天前
基于STM32的生活用水循环净化与浇灌系统(论文+源码)
stm32·软件工程·云平台·单片机毕业设计·单片机仿真·机械电子工程·伊犁师范学院
金字塔頂の蝸牛2 天前
每周GitCode开源项目推荐
开源·软件工程·ai编程
三环上的骑士2 天前
【领域篇17】软件研发与组织效能评估:100 个典型应用场景全景梳理(附分类体系)
软件工程·软件研发·代码质量·开发者体验·组织效能·效能评估·研发效能评估
郝学胜-神的一滴3 天前
游戏引擎原理与实践 01:聊聊游戏引擎的前世今生
开发语言·c++·游戏引擎·产品运营·软件工程·产品经理·untiy