参考资料:图说设计模式
参考资料:java-design-patterns
关于GOF设计模式,参考资料繁多。此文档不关注模式的实现细节,仅做对比总结。
为了保持完整性:
- 诸如,组合、桥接、单例这些简单的模式也收录并做了简单的描述。
- 同理,对于FP编程大火的今天,诸如:访问者,命令这样的模式其实已经没有多大意义,还是本着完整的原则做了整理。
- 实在不能理解,备忘录、解释器也能被归类到设计模式,分组到莫名其妙。
删除了性能优化型的模式,[享元模式]。这根本就是优化好吧,设计理论中有性能优化么?
基本原则
- 单一职责原则(Single Responsibility Principle, SRP)
- 开闭原则(Open-Closed Principle, OCP)
- 对扩展开放,对修改关闭
- 抽象化是开闭原则的关键
- 里氏代换原则(Liskov Substitution Principle, LSP)
- 所有引用基类(父类)的地方必须能透明地使用其子类的对象
- 子类不要改变父类的行为
- 尽量避免Override
- 依赖倒转原则(Dependency Inversion Principle, DIP)
- 抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程
- 接口隔离原则(Interface Segregation Principle, ISP)
- 使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口
- 即每个接口承担相对独立的角色,接口尽量小而专
- 迪米特法则(Law of Demeter, LoD)
- 一个软件实体应当尽可能少地与其他实体发生相互作用
- 少互动,低耦合,少麻烦。
结构型模式
1对1结构型
Adapter适配器:
- 1 in 1 结构
- A 持有B的抽象,C跟B没有直接关系,D实现B包含C,即D对C做了关于A的适配。
- 适配并重用已有的方法,适配第三方库降低耦合。
- 简单易懂,使用频繁
Decorator 装饰
- 1 in 1 结构
- 在原有对象上的包装或者增强
- 又是组合优于继承的好例子
- 一般而言,继承只用于设计抽象结构,很少会继承实类型做扩展。增强实类型,装饰是个好选择。
Proxy 代理
- 1 in 1 结构
- 动态代理的应用太多了,不解释了。
关于 代理,装饰,适配器三种 1in1结构模式的对比:
与原对象相关性 | 侧重点 | 目的 | |
---|---|---|---|
代理模式 | 极强,对外方法一致。 | 间接引用 | 访问控制、延迟加载、面向切面编程 |
装饰模式 | 强。类似继承的关联。 | 功能增强 | 非继承实现功能增强,灵活适配。 |
适配器模式 | 弱。只要满足适配规则即可。 | 兼容解耦 | 兼容老代码,兼容三方库。依赖适配接口而非实现。 |
多合一结构型
Composite 组合
- n combine 1 结构
- 多个对象组合成一个对象来用
- 简单易懂
Facade 外观
n combine 1 结构
这玩意不就是对外暴露的api接口么
-
一图见真相
-
Bridge桥接
- 1 associate n 结构
- 以插件的方式添加功能特性或扩展属性。
- 面向接口而非面向实现;组合优于继承
- 避免类型爆炸的常用手段
关于 组合,外观,桥接三种 多合一结构模式的对比:
侧重点 | 目的 | |
---|---|---|
组合模式 | 简单组合 | 实在没啥可说的了 |
外观模式 | 多子合一 | 对外统一接口 |
桥接模式 | 即插即用 | 依赖于抽象 |
构建型模式
1对多构建型
Simple Factory简单工厂
- 1 build n 型
- 基本的对象构建模式,构建逻辑都耦合在工厂类中
- 扩展产品需要修改工厂逻辑
- 只有工厂的产品做了抽象,工厂类自己是实类型
Factory Method工厂方法
- n build n 型
- 对工厂的产品以及生产产品的方法都做了抽象
- 通过实类型的工厂子类来决定具体的产品类型
- 在抽象的过程中,工厂的概念被淡化,仅做为一种抽象的约束
- 扩展性良好,无论是产品还是工厂都可以很好的扩展。
Abstract Factory抽象工厂
- 1 build n build n*m 型
- 结合了简单工厂和工厂方法从另一个角度做了更深层次的抽象。
- 工厂的选择,决定了产品族。选择工厂时类似简单工厂。
- 产品族内部的产品生产则是实现了抽象,类似工厂方法的实现。
- 具体情况请参看参考资料。
1对1构建型
Builder构建器
1 build 1 型
当一个对象过于复杂时,创建一个对象就变得麻烦起来。大量的重载构造器,setter,甚至还可能有init方法,使得使用者想要得到自己想要的对象还需要了解具体的实现。这个时候最好的解决办法就是,让专业的人做专业的事。
Builder可以在最终builder的时候做校验,对构建过程中的错误给出友好的提示。
对构建参数的校验独立出来,单一职责,代码更漂亮。
lombok天天用。
Prototype原型
- 1build1 型
- 创建对象最快捷的方法:有一个创建好的对象供复制。
- 想想我们做model的转换,BeanUtil copy,然后稍微改改属性。
- 作为原型的对象应该拥有,最抽象、最纯粹、最稳定不易变的属性。若使用原型创建对象之后还要大量覆盖之前的属性,
Singleton单例
- 1 build 1型
- 只有一个实例。
- 面试问的最多的设计模式。该模式概念及其简单,以至于问题的侧重点跑偏的如何保证实例的唯一性。
- final类,private构造器,privite static instance,public getter 最简单的解决方案。
- 其实一个全静态的类是不是也是单例呢?
行为型模式
1对1交互行为
Command命令
- 1notify 1
- 别名:动作(Action)模式,或事务(Transaction)模式
- 对消息指令做了抽象封装。消息指令本身包含了接受者,调用者本身不用关心接受者。
- 重点在于命令的undo,redo,命令组。也是为啥有事务别名的原因。
1对多交互行为
Observer 观察者
- 1 notify n
- 别名很多:发布-订阅(Publish/Subscribe)、模型-视图(Model/View)、源-监听器(Source/Listener)或从属者(Dependents)
- 适用于消息事件通知的模型,广泛用于各大框架,中间件,想不了解都难。
- 解耦神器,居家必备
Chain of responsibility责任链
- 1notify n
- 通过给多个对象处理请求的机会,避免请求的发送方与接收方耦合。将接收对象链起来,并沿着链传递请求。
- 链条上的每一个接收方自己决定是否处理请求,是否继续将链条传递下去。
- 责任链对比于观察者的不同点:处理逻辑与接收者顺序相关;可以截断消息;关心处理结果。
关于 观察者,责任链,命令三种 通知型模式的对比:
模式 | 通知方式 | 是否有反馈 | 侧重点 |
---|---|---|---|
观察者 | 广播,每一个订阅者都通知 | 无 | 解耦,1对n广播式通知 |
责任链 | 链式传播,可能会中途返回 | 有返回信息 | 层级式链条传播,寻找责任人,接收反馈 |
命令 | 一对一精准 | 无 | 动作的undo,redo,整体命令序列的控制 |
多对多交互行为
Mediator 中介者
- n notify n
- 多个同事对象的交互通通过中介方中转,而非直接交互。
- 同时对象关联中介的方式类似于注册。同事对象与中介者互相引用。
- 网状结构转化为星型结构,降低结构复杂度。
- 同事之间通过中介使用代号进行精准交互,或者不使用代号进行群组动作。
抽象
Strategy 策略
- 策略模式不关注行为动作的目标,而是关心行为本身的封装
- 简单来说就是对行为统一封装,上层依赖抽象
- 代码上很像很像桥接,只不过逻辑侧重点不相同
- 策略的装配仍然需要硬编码,与其定义一堆策略实体,直接使用Lambda传递策略函数或许是更好的选择。
Template-Method模板方法
- 做继承抽象时经常使用的设计模式,十分常用,十分有效,重点理解。
- 共性方法抽象到父类,具体实现留给子类扩展,以便最大程度的复用代码,更能精简逻辑。
- 不多说了,各大框架都有例子。
Iterator 迭代器
- 提供按顺序访问聚合对象的元素而不公开其底层表示的方法。
- Collection的迭代器基于此模式设计,烂熟于心了。
- 同上,不解释了。
State 状态
- 对状态做封装,即状态改变的伴生行为与不同状态下的行为被封装在状态类中。
- 我们经常用枚举或者直接用字符串做状态,实际上对状态的处理都放到状态拥有者中去做。这样对于 单据状态、审批状态这种通用性状态就会出现很多重复的代码逻辑。对于这种情况就可以使用状态模式做封装。
Visitor 访问者
- 动态的扩展对象的行为而不用修改对象本身。
- 通过在对象中定义通用的accept方法接受抽象的visitor来实现方法的动态扩展。同样的accept方法调用传入不同的visitor会有不同的行为。
- vistitor中会持有对象的实引用,使用不慎会产生循环调用。
- 随着fp编程的普及,访问者模式估计也会逐渐消失吧。
莫名其妙
Memento备忘录
- 存储状态以便恢复。。。
- 这玩意也能单独拿出来叫做设计模式么?
- 最保险的备忘方法还是持久化吧,这东西现在还能拿来干嘛。
Interpreter 解释器
- DSL的解析
- 正则表达式,各种format,语法解释器。
- 这东西也放到设计模式里么。。。。
结语
断断续续的整理完稿,回过头来看关于GOF23其实还是蛮简单的。
简单的模式,只要你有oo思想就能设计出来,不必学习也知道。
经久不衰的模式各大框架现在还在用,在实践中学习深化;
被编程模型淘汰的模式看看就好,不必深究;
莫名其妙的糟粕不理会就好,谁知道是什么历史原因遗留下来的。