跳转到内容

附录 A · 速查表与易混对比

这一附录是给翻查用的,可以单独打印。A.1 把 23 个模式各压成一行:它是什么、什么时候用、结构上抓哪一点、去哪一章看。A.2 把各章“容易混淆的模式”里的判别问题收在一张表里:两个模式拿不准时,回答那一个问题就能分开。星级和一句话与第 1 章总表一致;“什么时候用”一列的说法与第 28 章的决策表一致,要看某个模式什么时候别用,翻对应章的“什么时候别用”一节。

星级是本书作者的判断,依据是模式在主流框架和日常业务代码里出现的频率:★★★ 几乎每个项目都会碰到,★★ 在特定类型的系统里常见,★ 用得少,认得出即可。

创建型:对象怎么来

模式 星级 一句话 什么时候用 结构要点 章
工厂方法(Factory Method) ★★★ 让子类决定创建哪种对象 按类型 new 对象的 if 抄在好几个调用点,加一种类型每处都要改 创建者在固定流程里调工厂方法,具体创建者子类决定返回哪种产品 3
抽象工厂(Abstract Factory) ★★ 成套创建一组相互配合的对象 几个对象必须配套,各处各自 if 出来,配错了也不报错 一个工厂有多个创建方法,每个具体工厂产出同一族的一整套产品 4
建造者(Builder) ★★★ 分步骤组装一个参数很多的对象 构造函数参数越加越多,靠位置对应,传错位置不报错 按名字一步步设值,build() 时统一校验,产品造好不再变 5
单例(Singleton) ★★★ 保证一个类全局只有一个实例 创建一次很贵的对象被每个调用点各造一份 类自己用字段保存唯一实例,instance() 第一次调用时创建 6
原型(Prototype) ★ 复制一个现有对象来得到新对象 要一个和现成对象差不多的新对象,逐字段复制总漏字段 对象自己实现 clone(),决定每个字段怎么复制 7

结构型:对象怎么组合

模式 星级 一句话 什么时候用 结构要点 章
适配器(Adapter) ★★★ 把一个接口转换成调用方期望的接口 第三方接口和你要的对不上,转换代码散在每个调用处 适配器实现目标接口,内部持有被适配对象并转调 9
装饰器(Decorator) ★★★ 在不改原对象的前提下层层叠加功能 想给对象叠加压缩、加密这类功能,按组合写子类,数量成倍长 装饰器实现与被包对象相同的接口,同时持有一个该接口的对象 10
外观(Facade) ★★★ 给一组复杂的子系统提供一个简单入口 同一套“按顺序调几个子系统、失败回滚”的流程抄在几个入口 外观新定义一个更简单的方法,只有外观调子系统,反过来不行 11
代理(Proxy) ★★★ 用一个替身控制对真实对象的访问 权限、懒加载、审计这些把关的事散在调用处 代理与真实对象同接口并持有它,决定转不转、什么时候转 12
组合(Composite) ★★ 把对象组织成树,单个和整体一样对待 单个和一组要一样对待,组里还能再套组 叶子和容器实现同一个接口,容器持有一组该接口的孩子 13
桥接(Bridge) ★ 把两个独立变化的维度拆开再组合 两个维度各自会变,子类数是两边相乘 抽象部分持有实现部分的接口,两边各有自己的继承体系 14
享元(Flyweight) ★ 共享大量重复的细粒度对象以省内存 大量对象各带一份完全相同的数据 享元工厂按键缓存只读的内在状态,外在状态由使用者自己保存 15

行为型:对象怎么协作

模式 星级 一句话 什么时候用 结构要点 章
策略(Strategy) ★★★ 把一族可互换的算法各自封装 一族可互换的算法写成一串 if,好几处都要选 上下文持有一个策略接口,由外面挑好具体策略交给它 17
观察者(Observer) ★★★ 一个对象变化时自动通知所有关注者 一件事之后要做的事越来越多,全写在一个函数里 主题保存订阅者列表,发生变化时逐个通知,订阅者互不知道 18
命令(Command) ★★★ 把一个请求封装成对象 操作要撤销、排队、定时、记日志 命令存下请求参数和撤销所需信息,调用者决定何时执行或撤销 19
模板方法(Template Method) ★★★ 父类定流程骨架,子类填具体步骤 几段流程只有中间一两步不同,各抄了一份 父类的模板方法固定步骤顺序,子类只实现抽象步骤或覆写钩子 20
状态(State) ★★★ 对象的行为随内部状态切换 每个方法里都有 if status == …,加一个状态要翻遍 上下文把操作交给当前状态,具体状态做完事决定转到哪个状态 21
迭代器(Iterator) ★★★ 不暴露内部结构地逐个访问元素 翻页、游标写在每个调用方的循环里 聚合对象创建迭代器,迭代器记住走到哪里,只交出下一个 22
责任链(Chain of Responsibility) ★★ 请求沿一条处理者链传递 谁来处理写成一串 if,请求要依次过几道关 每个处理者持有下一个,自己决定处理还是交下去,调用方只认链头 23
中介者(Mediator) ★★ 用一个中间对象协调多个对象的交互 几个对象你调我、我调你,加一个要改好几个类 同事只持有中介者并通知它,中介者认识每个同事、写着联动规则 24
备忘录(Memento) ★ 保存并恢复对象的内部状态 要把对象恢复成之前的样子,字段由外面一个个抄出去 发起人创建并读回快照,负责人只保管,不读不改 25
访问者(Visitor) ★ 不改元素类就给它们加新操作 节点类型不常变,报表和操作不断加 元素只有 accept,访问者为每种元素各写一个 visit 方法 26
解释器(Interpreter) ★ 为一种简单语言定义文法并解释执行 规则要让运营自己写成一行文字 每条文法规则一个类,终结符直接读上下文,非终结符组合子表达式 27

每一行是两个(或几个)看着像的模式,加上各章给出的那个判别问题。一对模式在两章里都出现时只列一次,写出两个章号;只在一章出现的,写那一章。

创建型之间

容易混的模式 一个问题就能分清 章
简单工厂、工厂方法、抽象工厂 是一个函数,一个方法加一组子类,还是一个对象负责一整套产品 3、4
工厂方法、抽象工厂、建造者 造一个,造一套,还是分好几步造一个复杂的 4、5
建造者、原型 从零一项项填,还是从一个已经配好的对象出发、复制后只改几处 7
工厂方法、原型 是否要为每种产品准备一个子类;原型手里有一个现成对象就能造出同类新对象 7
单例、全局变量、模块级实例、依赖注入 谁创建、什么时候创建;用的人是到处直接拿,还是必须从参数里接 6
原型、备忘录 复制出来的东西给谁用:接着改的新对象,还是只交回 restore() 的快照 7、25

结构型之间

容易混的模式 一个问题就能分清 章
适配器、装饰器、外观、桥接 为什么包、对外接口变不变:适配器换接口,装饰器加功能,外观简化入口,桥接是事先拆开而不是事后补救 9、10、11、14
适配器、代理 接口换不换:适配器换接口,代理不换 12
装饰器、代理 去掉它,功能少了一样(装饰器),还是功能一样、只是访问没人把关了(代理) 10、12
装饰器、责任链 每一层都一定往下传吗 10、23
装饰器、组合 手里有几个孩子、套这一层为了什么:只包一个对象为加功能,还是一组孩子为让一组当一个用 13
外观、适配器 接口是谁定的、背后有几个对象:调用方先定好接口去对上它是适配器,替调用方新写更省事的方法是外观 11
外观、代理 外观定义新的、更简单的接口,背后是好几个对象;代理与原对象同接口,背后是一个对象 12
外观、中介者 删除测试:删掉它,子系统要不要改 11、24
桥接、策略 被持有的接口有几个方法,持有它的一方有没有自己的子类 14、17
享元、单例 整个类只有一个,还是每种一个、按需共享 15
享元、对象池 对象可变、同一时间借给一个使用者,还是同时被许多使用者引用、因此必须不可变 15
享元、缓存 为了省时间(可淘汰、淘汰了重算),还是为了省内存(大家用同一份) 15
组合、迭代器 要的是一个个节点,还是一个答案 13、22

行为型之间

容易混的模式 一个问题就能分清 章
策略、状态 谁来换:外面挑好交进去的是策略,状态做完事自己切到下一个的是状态 17、21
策略、模板方法 换算法要不要新建子类 17、20
策略、命令 这个对象会不会被存起来以后再用 19
模板方法、工厂方法 子类填的那一步是不是在创建对象 20
状态类、表驱动状态机 每一步除了换状态,还要做多少事 21
观察者、中介者 中间那个对象里有没有业务规则 18、24
观察者、发布订阅 publish 返回时,订阅者跑完了没有(有没有消息代理) 18
命令、备忘录 撤销时靠存下来的状态,还是靠反向操作 19、25
命令、责任链 关心的是请求长什么样,还是谁来接 23
迭代器、访问者 对每个节点做的事,要不要按节点类型分开写 22、26
组合、访问者 以后加得多的是节点还是操作 26
组合、解释器 这棵树是不是一句话 27
解释器、访问者 操作多不多 27
  • 先在 A.1 的“什么时候用”一列找到你的麻烦,再翻到那一章的“什么时候别用”一节,确认没有踩进去。
  • 两个模式看着像,先看 A.2 的问题能不能当场回答;回答不了,就按那一行的章号回去看对应的案例。
  • 同一个麻烦如果能用一个 if、一个函数或一张映射表说完,先别上模式。第 28.5 节和第 29.8 节把这条讲得更完整。