跳转到内容

第二部分 · 创建型模式

一句话:创建型模式解决“对象怎么来”的问题:让使用方不必到处写死 new 某个具体类,换实现、加参数、控制数量时,改动只落在一个地方。

创建对象看上去最简单:写一句 new 就行。麻烦是随着项目变大才冒出来的,常见的有三种。

第一种,new 散落各处。假设项目里到处是 new MySqlConnection(...),有一天要换成 PostgreSQL,你得用全局搜索找出每一处,一个个改。漏掉一处,那个角落的代码还连着旧数据库,程序不报错,数据却写错了地方。

第二种,构造参数越来越多。一个“发送邮件”的对象,起初只要收件人和正文,后来加了抄送、附件、优先级、重试次数,构造函数的参数列表拉到了十个。调用时 Mail("a@x.com", "你好", None, None, None, 3, True) 这样一长串,没人记得第六个位置是什么,传错了也不报错。

第三种,有些对象全局只能有一个。比如读取配置文件的对象,如果每个模块都自己 new 一份,就会各读各的,一个模块改了配置,别的模块还拿着旧值。你需要一个办法,让所有人拿到的是同一个。

这三种麻烦的共同点是:创建对象的那一行代码,和“创建哪个类、怎么配参数、创建几个”绑死了。创建型模式(Creational Patterns)做的事,就是把这一行背后的决定拆出来,放到一个专门的地方。

模式 一句话 流行度 先读哪个
工厂方法(Factory Method) 让子类决定创建哪种对象 ★★★ 先读
抽象工厂(Abstract Factory) 成套创建一组相互配合的对象 ★★
建造者(Builder) 分步骤组装一个参数很多的对象 ★★★
单例(Singleton) 保证一个类全局只有一个实例 ★★★ 先读
原型(Prototype) 复制一个现有对象来得到新对象 ★

流行度的含义和第 1 章一样:★★★ 是几乎每个项目都会碰到的,★ 是知道它解决什么问题、遇到时能认出来即可。

创建型模式:问题与模式对照

左边是你会遇到的问题,右边是该看的模式和章号,一条线对应一个。

  • 你想换一种实现,却发现 new 写了几十处,不想逐个去改 → 看第 3 章 工厂方法。
  • 你想一次换掉一整套配合使用的对象,比如换肤时按钮、输入框、对话框要一起换,不能一个新一个旧 → 看第 4 章 抽象工厂。
  • 你的构造函数已经有七八个参数,调用时分不清哪个是哪个 → 看第 5 章 建造者。
  • 你想让全局只有一个配置对象或连接池,所有模块拿到的是同一个 → 看第 6 章 单例。
  • 你想基于一个已经配好的对象复制出许多相似的新对象,而不是每次从头配一遍 → 看第 7 章 原型。

建议先读第 3 章工厂方法和第 6 章单例。工厂方法是整个创建型的起点,后面的抽象工厂是它的升级版,先把它读懂,抽象工厂就只剩“从一个对象变成一套对象”这一步差别。单例是五个模式里最常被人提到、也最常被滥用的,早点读到它的代价(全局状态让测试变难:一个测试改了全局配置没还原,下一个测试拿到的就是改过的值),对你之后判断该不该用很有帮助。

第 5 章建造者按需读。如果你手头正好有一个参数很长的构造函数,现在就读;如果没有,可以先跳过,等遇到那种代码时再回来,到时候你会很快认出它。第 4 章抽象工厂建议放在读完第 3 章之后再读,不要跳着读,否则会觉得它只是“更多的类”,看不出为什么需要它。

第 7 章原型可以跳过。它是五个模式里用得最少的,许多语言本身就带有复制对象的功能,日常写代码时很少需要专门套用这个模式。如果时间有限,读一下那一章开头的两行题记,知道它解决什么问题就够了。