第二部分 · 创建型模式
一句话:创建型模式解决“对象怎么来”的问题:让使用方不必到处写死
new 某个具体类,换实现、加参数、控制数量时,改动只落在一个地方。
为什么需要这一类模式
Section titled “为什么需要这一类模式”创建对象看上去最简单:写一句 new 就行。麻烦是随着项目变大才冒出来的,常见的有三种。
第一种,new 散落各处。假设项目里到处是 new MySqlConnection(...),有一天要换成 PostgreSQL,你得用全局搜索找出每一处,一个个改。漏掉一处,那个角落的代码还连着旧数据库,程序不报错,数据却写错了地方。
第二种,构造参数越来越多。一个“发送邮件”的对象,起初只要收件人和正文,后来加了抄送、附件、优先级、重试次数,构造函数的参数列表拉到了十个。调用时 Mail("[email protected]", "你好", None, None, None, 3, True) 这样一长串,没人记得第六个位置是什么,传错了也不报错。
第三种,有些对象全局只能有一个。比如读取配置文件的对象,如果每个模块都自己 new 一份,就会各读各的,一个模块改了配置,别的模块还拿着旧值。你需要一个办法,让所有人拿到的是同一个。
这三种麻烦的共同点是:创建对象的那一行代码,和“创建哪个类、怎么配参数、创建几个”绑死了。创建型模式(Creational Patterns)做的事,就是把这一行背后的决定拆出来,放到一个专门的地方。
本部分的模式
Section titled “本部分的模式”| 模式 | 一句话 | 流行度 | 先读哪个 |
|---|---|---|---|
| 工厂方法(Factory Method) | 让子类决定创建哪种对象 | ★★★ | 先读 |
| 抽象工厂(Abstract Factory) | 成套创建一组相互配合的对象 | ★★ | |
| 建造者(Builder) | 分步骤组装一个参数很多的对象 | ★★★ | |
| 单例(Singleton) | 保证一个类全局只有一个实例 | ★★★ | 先读 |
| 原型(Prototype) | 复制一个现有对象来得到新对象 | ★ |
流行度的含义和第 1 章一样:★★★ 是几乎每个项目都会碰到的,★ 是知道它解决什么问题、遇到时能认出来即可。
左边是你会遇到的问题,右边是该看的模式和章号,一条线对应一个。
遇到什么问题先看哪个
Section titled “遇到什么问题先看哪个”- 你想换一种实现,却发现
new写了几十处,不想逐个去改 → 看第 3 章 工厂方法。 - 你想一次换掉一整套配合使用的对象,比如换肤时按钮、输入框、对话框要一起换,不能一个新一个旧 → 看第 4 章 抽象工厂。
- 你的构造函数已经有七八个参数,调用时分不清哪个是哪个 → 看第 5 章 建造者。
- 你想让全局只有一个配置对象或连接池,所有模块拿到的是同一个 → 看第 6 章 单例。
- 你想基于一个已经配好的对象复制出许多相似的新对象,而不是每次从头配一遍 → 看第 7 章 原型。
怎么读这一部分
Section titled “怎么读这一部分”建议先读第 3 章工厂方法和第 6 章单例。工厂方法是整个创建型的起点,后面的抽象工厂是它的升级版,先把它读懂,抽象工厂就只剩“从一个对象变成一套对象”这一步差别。单例是五个模式里最常被人提到、也最常被滥用的,早点读到它的代价(全局状态让测试变难:一个测试改了全局配置没还原,下一个测试拿到的就是改过的值),对你之后判断该不该用很有帮助。
第 5 章建造者按需读。如果你手头正好有一个参数很长的构造函数,现在就读;如果没有,可以先跳过,等遇到那种代码时再回来,到时候你会很快认出它。第 4 章抽象工厂建议放在读完第 3 章之后再读,不要跳着读,否则会觉得它只是“更多的类”,看不出为什么需要它。
第 7 章原型可以跳过。它是五个模式里用得最少的,许多语言本身就带有复制对象的功能,日常写代码时很少需要专门套用这个模式。如果时间有限,读一下那一章开头的两行题记,知道它解决什么问题就够了。