第三阶段学习目标:理解常见的软件设计问题,以及如何使用抽象、组合、接口和多态控制复杂度。
核心思想:
不要背设计模式,要理解"为什么需要这种设计"。
目录
- [1. 设计模式到底解决什么?](#1. 设计模式到底解决什么?)
- [2. 第一原则:组合优于继承](#2. 第一原则:组合优于继承)
- [3. Strategy:策略模式](#3. Strategy:策略模式)
- [4. Factory:工厂模式](#4. Factory:工厂模式)
- [5. Adapter:适配器模式](#5. Adapter:适配器模式)
- [6. Facade:外观模式](#6. Facade:外观模式)
- [7. Observer:观察者 / 事件模式](#7. Observer:观察者 / 事件模式)
- [8. Command:命令模式](#8. Command:命令模式)
- [9. State:状态模式](#9. State:状态模式)
- [10. Dependency Injection:依赖注入](#10. Dependency Injection:依赖注入)
- [11. 设计模式真正应该学习什么?](#11. 设计模式真正应该学习什么?)
- [12. 设计模式的反模式](#12. 设计模式的反模式)
- [13. AI Coding 中如何使用设计模式?](#13. AI Coding 中如何使用设计模式?)
- [14. 本阶段最终能力](#14. 本阶段最终能力)
1. 设计模式到底解决什么?
设计模式不是为了让代码显得高级。
它解决的是反复出现的软件设计问题:
text
需求变化
↓
代码结构开始复杂
↓
找到变化点
↓
隔离变化
↓
减少影响范围
所以设计模式与前面的"高内聚、低耦合、封装变化"是一脉相承的。
2. 第一原则:组合优于继承
不要看到不同类型就马上建立复杂继承树。
很多时候:
text
继承
容易形成强耦合。
而:
text
组合
更灵活。
例如:
text
ModelService
├── Loader
├── Cache
├── CoordinateTransformer
└── Logger
ModelService 通过组合获得能力。
3. Strategy:策略模式
适合:
同一个业务流程存在多个可替换算法 / 实现。
例如 GIS 模型加载:
text
ModelLoader
├── OSGBLoader
├── Tiles3DLoader
├── GLTFLoader
└── OBJLoader
上层只依赖:
text
ModelLoader
4. Factory:工厂模式
适合:
创建对象的逻辑比较复杂,或者创建过程需要根据类型选择实现。
例如:
text
createModelLoader(type)
根据:
text
"osgb"
"3dtiles"
"gltf"
返回不同 Loader。
注意:
简单对象不要为了"设计模式"强行使用 Factory。
5. Adapter:适配器模式
适合:
两个接口不兼容,但你希望它们能够协作。
例如:
text
你的业务接口
↓
ModelLoader
↓
第三方 GIS SDK
如果第三方 SDK 的接口与你的业务接口不同,可以通过 Adapter 转换。
6. Facade:外观模式
适合:
一个复杂系统对外只需要暴露一个简单入口。
例如:
text
GISFacade
内部:
text
SceneManager
ModelManager
TerrainManager
CameraManager
CoordinateService
外部只需要:
text
gis.loadProject()
7. Observer:观察者 / 事件模式
适合:
一个事件发生后,多个模块需要响应,但不希望它们互相直接依赖。
例如:
text
ModelService
↓
model:loaded
↓
┌──────────┬──────────┐
↓ ↓ ↓
UI Logger SceneManager
非常适合:
- Electron
- 前端
- 状态变化
- GIS 场景事件
- 异步任务
8. Command:命令模式
把:
"一个操作"
封装成对象或独立命令。
例如:
text
LoadModelCommand
RemoveModelCommand
MoveModelCommand
适合进一步实现:
- Undo
- Redo
- 操作记录
- 批量执行
- 命令队列
GIS 编辑类软件尤其值得理解。
9. State:状态模式
当一个对象的行为会随着状态变化而变化时,可以显式建模状态。
例如:
text
Model
├── Idle
├── Loading
├── Loaded
├── Failed
└── Disposed
这比到处使用:
text
isLoading
isLoaded
isError
更容易控制复杂度。
10. Dependency Injection:依赖注入
核心思想:
不要让一个模块自己决定所有依赖。
例如不推荐:
text
ModelService
↓
new CesiumModelLoader()
可以考虑:
text
ModelService
↓
ModelLoader
然后外部注入:
text
ModelService(loader)
这样测试时可以传:
text
FakeModelLoader
从而降低耦合、提高可测试性。
11. 设计模式真正应该学习什么?
不要记:
text
Strategy = 策略
Factory = 工厂
Observer = 观察者
而要记:
text
什么问题?
↓
什么变化?
↓
哪里耦合?
↓
如何隔离?
例如:
为什么使用 Strategy?
不是因为:
"Strategy 是经典设计模式。"
而是因为:
这里有多个可替换算法,而且未来可能继续增加。
12. 设计模式的反模式
常见错误:
text
简单问题
↓
硬套设计模式
↓
Factory + AbstractFactory + Strategy + Adapter
↓
代码复杂度暴涨
最终:
设计模式解决的问题比原来的问题还复杂。
所以应该遵循:
先有问题,再有模式。
13. AI Coding 中如何使用设计模式?
不要问:
text
"这里帮我用设计模式优化。"
更好的问法:
text
当前代码存在多个模型格式。
请分析:
1. 这些实现有哪些变化点?
2. 是否存在高耦合?
3. Strategy / Factory / Adapter 哪种更适合?
4. 是否真的有必要引入设计模式?
5. 最简单的方案是什么?
这会训练你的设计判断能力。
14. 本阶段最终能力
能够看到一个问题时自然思考:
text
这里什么会变化?
↓
能不能封装?
↓
能不能通过接口隔离?
↓
能不能通过组合降低耦合?
↓
有没有必要使用某种设计模式?
最终目标:
不是会背设计模式,而是会识别设计问题。