本文用一句话简述各种设计模式, 旨在给自己做个备忘. 如果我工作中有用到过的场景, 我也顺带提一嘴, 因为其实你对代码在进行各种封装, 分拆, 优化等的时候, 你用的方法就是别人抽象出来的某种设计模式之一. 所以并不是先学设计模式才会架构代码.
比如, 以前我的老板总是要我把任何第三方库都按他的要求包一层, 这样输出的接口风格都是一样的, 也是他最容易理解的. 老板在提这个要求的时候, 不会是因为"我要用界面模式来重构", 而是真的就是要我封装成更易理解的API.
- 创建型: 这类模式提供创建对象的机制, 能够提升已有代码的灵活性和可复用性。
- 结构型:这类模式介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。
- 行为模式:这类模式负责对象间的高效沟通和职责委派。
创建型模式
工厂模式
用create代替new, 可以通过工厂方法来创建(同类不同性质的)对象, 比如new Ship()
, new Truck()
和CreateTranport()
, 哪个扩展性更高?
生成器(构造者)
将一个复杂对象的构建与它的表示分离, 使得同样的构建过程可以创建不同的表示.
相比工厂模式创建船只还是卡车, 如果只是创建卡车, 要个性化, 一般靠一堆入参, 生成器模式建议将对象构造代码从产品类中抽取出来, 并将其放在一个名为生成器的独立对象中。
- create house
- build walls
- build doors
- build windows
- ...
原型模式
- 用原型实例指定创建对象的种类, 并且通过拷贝这些原型创建新的对象.
- 原型模式将克隆过程委派给被克隆的实际对象。 (即不由第三方来克隆一个对象)
单例模式
- 保证一个类只有一个实例, 并且提供一个全局访问点.
- 为什么会有人想要控制一个类所拥有的实例数量? 最常见的原因是控制某些共享资源 (例如数据库或文件) 的访问权限。
- 将默认构造函数设为私有, 防止其他对象使用单例类的 new运算符。
- 新建一个静态构建方法作为构造函数。 该函数会 “偷偷” 调用私有构造函数来创建对象, 并将其保存在一个静态成员变量中。 此后所有对于该函数的调用都将返回这一缓存对象。
结构型模式
适配器模式
将一个类的接口转换成客户希望的另一个接口. 适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.
桥接模式
将抽象部分与它的实现部分分离. 没看懂, 示例是用组合来代替继承, 比如抽象成颜色和形状两个类, 这样可以组成出来各种颜色的形状
组合模式
将对象组合成树形结构以表示“部分-整体”的层次结构. 组合模式使得用户对单个对象和组合对象的使用具有一致性.
示例, 一堆盒子里装着不同的产品, 要计算总价, 你可以遍历, 也可以让每个盒子负责自己的总价, 这样我们只要计算盒子的总价, 而无需关心每个"部分"自己的实现
至少在这个例子中, 有个特性, 即自己是一种东西(订单), 组合到一起也是一样的东西(订单), 虽然生产中都是组合在一起呈现的, 但自己也要实现组合后的类型的一些功能, 比如算总价
装饰者模式
动态地给一个对象添加一些额外的职责.
- 就增加功能来说, 装饰者模式相比生成子类更为灵活.
- 就实现来说, 由继承一个类变成了"包装"一个类, 其实就是把原来的功能包到一个新对象里去
外观模式
为子系统中的一组接口提供一个一致的界面, 外观定义了一个高层接口, 这个接口使得这一子系统更加容易使用.
一般我们把第三方组件包装一些便捷方法, 也可以理解为外观模式, 这样调用者就不需要关心第三方组件的实现了, 类似适配器, 多了个中介
享元模式
通过共享多个对象所共有的相同状态, 让你能在有限的内存容量中载入更多对象。
比如一个对战游戏, 每一粒子弹是一个类实例, 有形状位置贴图等等数据, 满屏幕的子弹内存就吃不消了, 通过观察可以发现子弹的贴图和形状什么的都是一样的, 只需要根据是子弹还是炮弹做个简单区分, 没必要把这么消耗的资源存到每一个实例里
代理模式
为其他对象提供一种代理以控制对这个对象的访问. 为什么要控制对于某个对象的访问呢? 举个例子: 有这样一个消耗大量系统资源的巨型对象(数据库连接), 你只是偶尔需要使用它, 并非总是需要。
解决: 在客户端和数据库间加个代理, 代理可处理延迟初始化和缓存查询结果等工作
- 信用卡是银行账户的代理
- 银行账户是现金的代理
行为模式
责任链模式
感觉更像一个工作流, 比如登录, 鉴权, 过滤重复IP等等, 全部揉在一起和依次有序各做各的区别
命令模式
将一个请求封装为一个对象, 从而可以用不同的请求对客户进行参数化;
其实封装GUI的事件就是一个命令模式, 不同的GUI按钮可以完成同一个任务, 而不是为每一个功能子类化一个Button, 而且万一要改变"复制"的行为, 还得跑到每一个子类里面去改
迭代器模式
提供一种方法来访问一个聚合对象中的各个元素, 而又无须暴露该对象的内部表示(列表, 栈, 树等).
中介者模式
用一个中介对象来封装一系列的对象交互. 中介者使各对象不需要显式地相互引用, 从而使其耦合松散, 而且可以独立地改变它们之间的交互.
把相互引用相互依赖改为中介引用所有人, 所有人只引用中介. 组件化和解耦常用
备忘录模式
在不破坏封装性的前提下, 捕获一个对象的内部状态, 并在该对象之外保存这个状态. 这样以后, 就可以在不破坏封装性的前提下, 将该对象恢复到原先保存的状态. 比如设计文档编辑器, 需要能追溯和回滚每一步的操作, 这些快照需要由文档类来提供
观察者模式
- 允许你定义一种订阅机制, 可在对象事件发生时通知多个 “观察” 该对象的其他对象。
- 核心就是三件事: 订阅, 发布, 和查话询订阅对象.
- 次要的当然还有解除订阅等功能
状态模式
- 允许一个对象在其内部状态改变时改变它的行为. 对象看起来似乎修改了它的类.
- 比如一个文档的不同状态下, 对应不同的方法, 而不是把这些方法实现在文档类中
- 比如手机在解锁和未解锁状态下, 同一按钮对应不同的功能(而不是在里面不停地做if-else)
策略模式
- 定义一系列算法, 将每一个算法封装起来, 并使它们可以相互替换.
- 等于入口类只需要
set strategy
, 和execute
, 真正的执行则由strategy来负责 - 比如设计导航类, 你只需要关心起点终点和途径点, 以及设定是骑行还是导航, 真天上的路径规划在每个策略算法中
模板方法模式
- 定义一个操作中的算法的骨架, 而将一些步骤延迟到子类中. 模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤.
- 跟生成器把一个构造变成N个构建类似, 模板方法将一个流程变成N个step, 区别就在于build没有先后, 而step则顺序相关
- 比如解析文档的类, 可以标准化为打开文档, 解析文档, 抽象出数据结构, 解析数据, 生成报告等, 前几步根据文档类型都吧可以个性化, 因此可以交由子类自行实现
- 上述2和3说的不是一回事, 一个是自由组合step, 一个是从既定步骤里找到需要个性化的步骤把它抽象, 由子类延迟实现
访问者模式
它能将算法与其所作用的对象隔离开来。