【设计模式】设计模式总结 ( 七大设计原则 | 创建型模式 | 结构型模式 | 行为型模式 ) ★★★
文章目录
一、七大设计原则
1、开闭原则
2、依赖倒置原则
3、单一职责原则
4、接口隔离原则
5、迪米特原则
6、里氏替换原则
7、合成复用原则
二、创建型模式
0、简单工厂模式 ( 不属于 GOF 设计模式中 )
1、工厂方法模式
2、抽象工厂模式
3、建造者模式
4、单例模式
5、原型模式
三、结构型模式
1、适配器模式
2、装饰者模式
3、代理模式
4、外观模式
5、桥接模式
6、组合模式
7、享元模式
四、行为型模式
1、策略模式
2、观察者模式
3、责任链模式
4、备忘录模式
5、模板方法模式
6、迭代器模式
7、中介者模式
8、命令模式
9、访问者模式
10、解释器模式
11、状态模式
一、七大设计原则
1、开闭原则
【设计模式】软件设计七大原则 ( 开闭原则 )
开闭原则 :
定义 : 一个
软件实体
,
类 / 模块 / 函数
, 对
扩展
开放
, 对
修改
关闭 ;
抽象与实现 : 用
抽象
构建框架
, 用
实现
扩展细节 ;
优点 : 提高 软件系统 的
可复用性 及 可维护性 ;
开闭原则 是 面向对象 设计 中 , 最基础的 设计原则 , 它指导我们建立稳定灵活的系统 ;
开发新功能时 , 尽量
不修改原有的代码
, 尽量
使用扩展增加新功能 ;
实现 开闭原则 的核心思想 是面向抽象编程 , 不是面向实现编程 ;
定义的
对象类型
是
抽象类类型 或 接口类型
,
调用的方法
是
抽象类 或 接口 中的方法 ;
抽象是
稳定的
, 让类依赖于抽象 ,
对于修改来说就是封闭的 ;
通过 面向对象 的
继承
, 以及
多态机制
, 可以实现
对 抽象 的 继承
, 通过
重写改变其固有方法
, 或
实现新的扩展方法 ;
2、依赖倒置原则
【设计模式】软件设计七大原则 ( 依赖倒置原则 | 代码示例 )
依赖倒置原则 :
高层模块
不应该 依赖
低层模块
, 二者都应该
依赖其抽象
;
抽象 不应该 依赖细节 ,
细节应该依赖抽象 ;
针对接口编程 , 不要针对实现编程 ;
通过抽象 , 包括使用
接口 或 抽象类
, 使个各类或模块之间
彼此独立 , 互不影响
, 从而实现模块之间的
松耦合
,
降低模块间的耦合性 ;
使用依赖倒置原则时的注意点 :
每个类都 尽量
实现自接口
或
继承抽象类 ;
尽量
避免从具体的类派生 ;
尽量
不要覆盖基类方法 ;
依赖倒置原则的优点 : 减少类之间的
耦合性
, 提高系统
稳定性
, 提高
代码可读性
,
可维护性
, 可
降低修改程序所造成的风险 ;
3、单一职责原则
【设计模式】软件设计七大原则 ( 单一职责原则 | 代码示例 )
单一职责原则 : 不要存在
多余一个
导致
类变更的原因 ;
假设有一个类 , 负责 2 2 2 个职责 , 职责 1 1 1 和 职责 2 2 2 ;
一旦
需求发生变更
, 如 职责 1 1 1 相关功能发生改变 ;
修改该类的 职责 1 1 1 功能时
,
有可能导致原本运行正常的职责 2 2 2 发生故障 ;
对于上述类 , 应该
分别针对 职责 1 1 1 和 职责 2 2 2
,
各自建立一个独立的类
, 这样就保证了系统的稳定性 ;
这样修改 职责 1 1 1 和 职责 2 2 2 中的任何一个功能 , 都不会影响另外一个职责的功能 ;
推荐的开发方法 : 使一个
类 / 接口 / 方法
只负责一项职责 ;
单一职责优点 : 提高
类的 可读性
, 提高
系统的 可维护性
, 降低
类的复杂度
, 降低
变更引起的风险 ;
类越简单
,
可读性越好
,
同时提高了可维护性 ;
一个类只负责一个职责 , 比负责多个职责 , 类要
简单得多 ;
变更是必然的 , 必须要接收变更 , 如果
单一职责原则遵守的好
, 当修改一个功能时 , 可以
显著降低对其它功能的影响 ;
单一职责原则 不只是 面向对象 设计中特有的职责 , 只要是模块化的系统 , 都适合使用单一职责原则 ;
4、接口隔离原则
【设计模式】软件设计七大原则 ( 接口隔离原则 | 代码示例 )
接口隔离原则 : 用
多个
专门的 接口
, 不使用
单一 的总接口
, 客户端
不应该依赖
它
不需要的 接口 ;
一个类 对 另一个类 的依赖 , 应该建立在 最小接口 上 ; 如果 有一个
大接口
, 里面有
很多方法
, 如果使用一个类
实现该接口 ,
所有的类都要实现 ;
建立 功能 单一接口 , 不要建立 庞大 臃肿 的接口 ;
尽量细化接口 , 接口中的方法尽量少 ;
接口设计适度原则 : 接口隔离原则 中 最重要的就是 注意
适度原则
, 一定要适度 ;
接口设计的
过大 , 过小
,
都不合适
; 设计接口时 , 多花时间去思考策划 ;
接口方法 尽量少 , 但要有限度 ,
对接口进行细化
, 肯定能
提高系统设计的灵活性
, 但是如果
接口设计的过小 , 方法过少
, 则会
造成接口数量过多 , 提高整个程序设计的复杂性 ;
接口隔离原则 优点 : 符合
高内聚
,
低耦合
的
设计思想
, 使得类具有很好的
可读性 ,
可扩展性 ,
可维护性 ;
降低耦合 : 平时设计接口时 ,
只暴露客户端需要的方法
, 客户端不需要的方法 , 直接隐藏起来 ; 只有专注的为一个模块提供定制服务 , 才能
建立最小的依赖关系
, 这样就降低了耦合程度 ;
提高内聚 : 减少对外交互 ,
使用接口中最少的方法 ,
完成最多的事情 ;
实际开发中 , 实践接口隔离原则时 , 也要根据业务场景 , 业务模型 , 以及以后有可能会发生变更的地方 , 对于这些做一些预判 , 抽象出业务模型很重要 ;
5、迪米特原则
【设计模式】软件设计七大原则 ( 迪米特原则 | 代码示例 )
迪米特原则 又称为
迪米特法则 ,
最少知道原则 ,
最少知识原则 ;
定义 :
一个对象
应该 对
其它对象
,
保持最少的了解 ;
尽量 降低 类之间的耦合 ;
对外部引入的类 , 越少越好 ;
迪米特原则优点 :
降低了 类 之间的耦合 ;
代码实现的层面最佳做法 : 尽量不要对外公开太多的 public 方法 , 和 public 变量 , 尽量内敛 , 多使用 private / protected 权限 ;
迪米特原则 的 核心观念 : 就是
类 之间的解耦
, 解耦 是有一定程度的 , 尽量做到
弱耦合
,
耦合程度越低 ,
类的复用率 才能提高 ;
由于 减少了
类之间 不必要的依赖
, 从而达到了
降低了 耦合 的目的 ;
适度使用 : 使用 迪米特原则 要
适度
, 如果
过分使用迪米特原则
, 会
产生大量中介类 , 导致
系统变复杂 ,
增加维护难度 ;
使用 迪米特原则 要权衡利弊 , 既要做到
结构清晰
, 又要做到
低耦合
高内聚 ;
如果存在一个方法 , 既可以放在 A 类中 , 又可以放在 B 类中 ;
这种情况下 , 如果一个方法 可以放在本类中 , 既
不增加类间关系
, 也
没有对本类产生负面影响
, 就可以放到本类中 ;
方法中 , 如果 参数 , 返回值 , 使用了其它类 , 导致了
import 导入了一个依赖
, 这就
增加了类间的关系 ;
迪米特原则 强调 只和朋友交流 , 不和陌生人说话 ;
这里的朋友 指的是 : 出现在
成员变量
, 方法的
输入
,
输出参数
中的类 , 称为成员朋友类 , 出现在方法体内部的类 , 不属于朋友类 ;
也就是说 类 A , 我使用了 类 A 中的方法 , 或成员 , 尽量避免导致本类 import 导入新的类 ;
6、里氏替换原则
【设计模式】软件设计七大原则 ( 里氏替换原则 | 定义 | 定义扩展 | 引申 | 意义 | 优点 )
【设计模式】软件设计七大原则 ( 里氏替换原则 | 代码示例 | 类示例 | 方法入参示例 | 方法返回值示例 )
里氏替换原则定义 :
如果 对每一个
类型为 T1
的
对象 o1
, 都有
类型为 T2
的
对象 o2 ,
使得
以 T1 定义的
所有程序 P
在
所有对象 o1
都
替换成 o2 时 ,
程序 P
的
行为 没有发生变化 ,
那么
类型 T2
是
类型 T1
的
子类型 ;
符号缩写说明 : T 是
类型 Type
, o 是
对象 Object
, P 是
程序 Program ;
通俗理解 :
T1 类
生成
o1 对象 ,
T2 类
生成
o2 对象 ,
开发的
程序 P
中
使用了 T1 类型
,
使用 T1 创建了对象 o1 ,
将程序中
所有的 o1
都替换成
T2 o2 时 ,
程序 P 的行为 , 没有发生变化 ,
可以认为
T2 是 T1 的子类型 ;
T2 是 T1 的子类型 , T1 则是 T2 的父类 ;
里氏替换原则 是
继承复用
的基石 , 只有当
子类 可以 替换
父类 , 并且
软件功能不受影响
时 ,
父类才能真正的被复用
, 子类也能在父类的基础上
增加新的行为 ;
里氏替换原则 是对
开闭原则
的补充 , 实现开闭原则的关键是 进行抽象 , 父类 和 子类 的继承关系 , 就是 抽象 的具体实现 ;
里氏替换原则引申意义 :
子类 可以
扩展
父类的功能
, 但是绝对不能
改变
父类原有的功能 ;
子类 可以
实现
父类的
抽象方法
, 但是
不能
覆盖
父类的
非抽象方法 ;
子类中 可以
增加
自己特有的方法 ;
重载 ( 输入参数 宽松 ) : 子类的方法
重载
父类的方法
时 , 方法的前置条件 ( 输入参数 ) , 要比
父类方法的输入参数更宽松 ;
如 : 父类的参数是 HashMap , 如果要符合 里氏替换原则 , 子类如果重载父类方法 , 那么需要使用 Map 类型参数 ;
( 这里注意区分 重写 与 重载 , 重写是重写父类方法 , 重载是函数名相同 , 参数不同 )
重写 ( 返回值 严格 ) : 当 子类的方法
重写 / 重载 / 实现
父类的方法时 , 方法的
后置条件 ( 返回值 )
要
比父类更严格或相等 ;
如 : 父类的返回值是 Map , 子类的相同的方法 是 Map 或 HashMap ;
7、合成复用原则
【设计模式】软件设计七大原则 ( 合成复用原则 | 代码示例 )
合成复用原则
又称为
组合复用原则
,
合成/聚合复用原则 ,
组合/聚合复用原则 ;
合成复用原则定义 : 想要达到
软件复用
的目的 , 尽量使用
对象 组合/聚合
, 而不是
继承关系 ;
聚合 是 has-A 关系 ; ( 关系较弱 )
代表部分事物的对象 ( 次 )
与
代表聚合事物的对象 ( 主 )
生命周期无关 , 删除了聚合对象 , 不代表删除了代表部分事物的对象 ;
组合 是 contains-A 关系 ; ( 关系较强 ) 一旦删除
代表组合事物的对象 ( 主 )
, 那么
代表部分事物的对象 ( 次 )
也一起被删除 ;
继承 是 is-A 关系 ;
电脑 与 U 盘 是聚合关系 , 电脑没了 , U 盘可以独立存在 , 还可以接在其它电脑上 ;
A 类中包含了 B 类的引用 ,
当 A 类对象销毁时
,
B 类引用所指向的对象也一同消失
, 没有任何一个引用指向他 , 该引用成为了垃圾对象 , 被回收 ;
这种情况就是 组合 ;
加入 A 类销毁后 ,
B 类对象还有在其它位置被引用 ,
B 类对象不会被销毁 ,
此时这种关系就是 聚合 ;
二、创建型模式
工厂方法模式 , 抽象工厂模式 , 建造者模式 , 单例模式 , 使用频率较高 ;
原型模式 使用频率较低 ;
0、简单工厂模式 ( 不属于 GOF 设计模式中 )
【设计模式】简单工厂模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
简单工厂模式 : 由 一个
工厂对象
决定 创建出
哪一种
产品类
的
实例 ;
简单工厂模式类型 : 创建型 ;
简单工厂模式适用场景 :
创建对象少 :
工厂类
负责
创建的对象
比较少 ;
不关心创建过程 :
客户端
只知道
传入 工厂类 的参数
, 对于
如何创建对象
不关心 ;
简单工厂模式优点 : 只需要传入
正确的参数
, 就可以
获取需要的对象
,
无需知道创建细节 ;
工厂类中有必要的
判断逻辑
, 可以决定
根据当前的参数
创建对应的产品实例
,
客户端可以免除直接创建产品对象的责任 ;
通过该模式 , 实现了对
创建实例
和
使用实例
的
责任分割
;
提供专门的
工厂类
用于创建对象 ,
客户端
无需知道所创建的产品类的类名 , 只需要知道对应产品类的参数即可创建对象实例 ;
简单工厂模式缺点 :
工厂类
职责 过重
, 如果要增加新的产品 , 需要
修改工厂类的判断逻辑
,
违背 " 开闭原则 " ;
7 7 7 大设计原则 ,
不能全部遵守 ,
也不能不遵守 ,
注意平衡 功能 和 系统复杂度 , 找到最合适的一个点 ;
1、工厂方法模式
【设计模式】工厂方法模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
工厂方法模式 : 定义一个
创建对象
的
接口
, 让
实现这个接口的子类
决定
实例化哪个类
, 工厂方法让
类的实例化
推迟到子类中进行 ;
工厂方法模式类型 : 创建型 ;
创建 实例对象
过程可能会很复杂
, 有可能会
导致大量的重复代码
, 工厂方法模式 通过 定义 一个单独的 创建 实例对象 的方法 , 解决上述问题 ;
通过 子类 实现 这个方法 , 创建具体的 实例对象 ;
工厂方法模式适用场景 :
重复代码 : 创建对象 需要使用
大量重复的代码 ;
不关心创建过程 : 客户端
不依赖 产品类
,
不关心 实例 如何被创建 , 实现等细节 ;
创建对象 : 一个类
通过其 子类
来
指定 创建哪个对象 ;
客户端
不需要知道
具体 产品类 的 类名
, 只需要知道
所对应的工厂
即可 , 具体的产品对象 , 由对应的工厂创建 ,
客户端只需要知道 产品 对应的 工厂 ;
工厂方法模式 利用了 面向对象 的
多态性
, 和
里式替换 原则
;
子类对象 覆盖 父类对象 , 使 系统
更容易扩展
, 将
创建对象的过程
推迟到子类实现
, 创建对象的任务 ,
委托给 多个 工厂子类 中的某一个 ,
客户端不需要关心是哪个 工厂子类 创建的 产品对象 ;
工厂子类 一般都是 需要的时候 ,
动态指定 ;
工厂方法模式优点 :
不关心创建细节 : 用户 只需要
关心 所需产品 对应的工厂 ,
无需关心创建细节 ;
符合开闭原则 : 加入 新产品 ,
符合开闭原则 ,
提高可扩展性 ;
工厂方法模式 中 ,
使用 工厂类创建 产品对象
, 同时
隐藏了 具体的 产品类 被 实例化 的细节 ;
工厂方法模式缺点 :
增加复杂性 :
类的个数容易过多
,
增加系统复杂度 ;
在 添加新产品 时 ,
除了编写 新的产品类 之外
,
还要 编写该产品类对应的 工厂类 ;
增加难度 : 增加了系统
抽象性
和
理解难度 ;
工厂方法本身
利用了抽象
, 该模式中会
引入抽象层
, 如果要动态创建产品类 , 还要
引入反射技术 ;
设计模式 的 使用 , 要根据 实际的 业务场景 , 模型 综合平衡考量 , 不能过分遵守设计原则 和 设计模式 ;
2、抽象工厂模式
【设计模式】抽象工厂模式 ( 简介 | 适用场景 | 优缺点 | 产品等级结构和产品族 | 代码示例 )
抽象工厂模式 : 提供 一个
创建
一系列
相关 或 相互依赖
对象
的接口 ;
创建目标对象时 , 只需要直到对象的抽象类型或接口类型即可 , 不需要知道具体的实现类型 ;
抽象工厂模式类型 : 创建型 ;
抽象工厂模式 可以将
一组具有同一主题
,
单独的工厂
封装起来
;
在使用时 ,
客户端
创建
抽象工厂 的实现
, 使用 抽象工厂 作为接口 , 来创建这一主题的对象 ;
使用的时候 , 不需要知道
从内部 工厂方法中获得的 对象 的具体类型
;
客户端 只 使用这些对象的
通用接口 ;
抽象工厂模式 实现了 一组对象的
实现细节
与
使用
分离 ;
抽象工厂模式适用场景 :
忽略创建细节 : 客户端 不关心 产品实例
如何 被创建 ,
实现等细节 ;
创建产品族 : 强调
一系列 相关的 产品对象
, 一般是
同一个产品族
,
一起使用 创建对象需要大量重复的代码 ;
产品类库 : 提供 一个
产品类 的库
, 所有的产品 以
同样的接口出现
, 使
客户端不依赖于具体实现 ;
使用抽象工厂模式 , 可以在工厂变化时 , 不需要修改 客户端 使用工厂的 代码 ;
抽象工厂模式优点 :
隔离产品代码 : 在
应用层
隔离
具体产品的代码
, 客户端
无须关心 产品创建 的细节 ;
创建产品族 : 将
一个系列 的 产品族 ,
统一到一起创建 ;
抽象工厂模式缺点 :
扩展困难 : 规定了 所有
可能 被创建
的
产品集合
,
产品族
中
扩展 新的产品 困难
, 需要
修改抽象工厂的接口 ;
增加难度 : 增加了系统的
抽象性
和
理解难度 ;
产品等级结构和产品族 :
下图中 , 有 椭圆形 , 圆形 , 正方形 , 三种产品 ;
产品族 : 相同颜色的代表一个产品族 ;
产品等级结构 : 相同形状的代表同一个产品等级结构 ;
如 : 方型 - 洗衣机 , 圆形 - 空调 , 椭圆 - 冰箱 ;
横向 看 产品族 : 某 品牌下 有 方型 - 洗衣机 , 圆形 - 空调 , 椭圆 - 冰箱 , 这是一个产品族 ;
纵向看产品等级结构 : 椭圆 - 冰箱 , 纵向指的是不同品牌的冰箱 ;
工厂方法模式 针对的是
产品等级结构
, 可以
扩展多个相同的产品 ;
抽象工厂模式 针对的是
产品族
, 在 某个品牌 的工厂中取出 洗衣机 类 , 取出的肯定是 该品牌的洗衣机实例对象 ;
只要指出 产品所在的 产品族 以及 产品所在的 产品等级结构 , 就可以唯一确定一个产品 ;
左侧的 纵坐标轴 上的 工厂 是
具体的工厂
, 从该具体的工厂中
创建的实例
是
该产品族中的实例 ;
使用 工厂模式 还是 抽象工厂模式 , 要看具体的业务场景 ;
当一个工厂 可以 创建
分属于
不同 产品等级结构
的
一个 产品族
中的
不同对象时
, 使用
抽象工厂模式 ;
如 :
工厂 中可以创建
相同品牌的 洗衣机 , 冰箱 , 空调
等产品 , 使用 抽象工厂模式 ;
如果 工厂中创建
不同品牌的空调
, 则使用 工厂方法模式 ;
3、建造者模式
【设计模式】建造者模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
建造者模式 : 将
一个复杂对象
的
构建过程
与其
表示
分离 , 使得
同样的构建过程
, 可以
创建不同的表示 ;
用户只需要
指定
需要建造的类型
就可以
得到该类型对应的产品实例
, 不关心建造过程细节 ;
建造者模式就是 如何逐步构建包含多个组件的对象 ,
相同的构建过程 ,
可以创建不同的产品 ,
建造者模式类型 : 创建型 ;
建造者模式适用场景 :
结构复杂 : 对象 有
非常复杂的内部结构 ,
有很多属性 ;
分离创建和使用 : 想把 复杂对象 的
创建
和
使用
分离 ;
当创造一个对象 需要很多步骤时 , 适合使用建造者模式 ;
当创造一个对象 只需要一个简单的方法就可以完成 , 适合使用工厂模式 ;
建造者模式优点 :
封装性好 :
创建
和
使用
分离 ;
扩展性好 : 建造类之间
相互独立
, 在
一定程度上解耦 ;
建造者模式缺点 :
增加类数量 :
产生多余的 Builder 对象 ;
内部修改困难 : 如果
产品内部发生变化
, 建造者也要相应修改 ;
建造者模式 与 工厂模式 :
注重点不同 : 建造者模式 更注重于
方法的调用顺序
; 工厂模式 注重于
创建产品
, 不关心方法调用的顺序 ;
创建对象力度不同 : 创建对象的力度不同 ,
建造者模式可以创建复杂的产品 ,
由各种复杂的部件组成 ,
工厂模式创建出来的都是相同的实例对象 ,
4、单例模式
5、原型模式
【设计模式】原型模式 ( 概念简介 | 使用场景 | 优缺点 | 基本用法 )
【设计模式】原型模式 ( 浅拷贝 | 深拷贝 | 原型与单例冲突 | 禁用 final )
原型模式 :
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
① 设计模式类型 :
创建型设计模式 ;
② 原型实例对象 :
给出原型实例对象 , 根据该对象创建新对象 ;
③ 创建对象类型 :
创建对象的种类由原型的实例对象类型确定 ;
④ 创建方式 :
不调用构造函数 , 而是通过克隆原型的实例对象 , 使用现有对象创建另一个相同类型的对象 , 隐藏创建细节 ;
原型模式使用场景 :
原型模式的目的是
降低实例对象个数 , 减少构造函数的调用次数 ;
① 类初始化消耗资源过多 :
如果类初始化时消耗过多的资源 , 如这个类中某个成员占用大量内存 , 为了节省开销 ;
② 初始化繁琐耗时 :
类对象创建时经过大量的计算 , 或与本地资源 ( 数据库 , 文件 ) 频繁交互 , 每次创建消耗大量的 CPU 与 时间资源 ;
③ 构造函数复杂 :
类中定义的构造函数复杂 ;
④ 实例对象数量庞大 :
如果在内存中循环创建了很多该实例对象 , 就可以使用原型模式复用不用的对象 , 用于创建新对象 ;
1 . 原型模式优点 :
性能高 , 简单 ;
① 性能高 :
使用原型模式复用的方式创建实例对象 , 比使用构造函数重新创建对象性能要高 ;
( 针对类实例对象开销大的情况 )
② 流程简单 :
原型模式可以简化创建的过程 , 可以直接修改现有的对象实例的值 , 达到复用的目的 ;
( 针对构造函数繁琐的情况 )
2 . 原型模式缺点 :
实现复杂 , 坑多 ;
① 覆盖 clone 方法 ( 必须 ) :
必须重写对象的 clone 方法 , Java 中提供了 cloneable 标识该对象可以被拷贝 , 但是必须覆盖 Object 的 clone 方法才能被拷贝 ;
② 深拷贝 与 浅拷贝 风险 :
克隆对象时进行的一些修改 , 容易出错 ;
需要灵活运用深拷贝与浅拷贝操作 ;
三、结构型模式
适配器模式 , 装饰者模式 , 代理模式 , 外观模式 , 桥接模式 , 享元模式 使用较频繁 ;
组合模式不经常使用 ;
1、适配器模式
【设计模式】适配器模式 ( 概念 | 适用场景 | 优缺点 | 外观模式对比 | 适配器模式相关角色 | 类适配器 | 对象适配器 | 实现流程 )
适配器模式 :
① 设计模式类型 :
结构型 ;
② 概念 :
将 类的接口 转换成用户可以调用的 另外一个接口 ;
③ 目的 :
使接口不兼容的两个类可以一起工作 ;
④ 概念中的三个角色 :
被适配者 ( 现有的功能类 ) ,
用户目标接口 ( 用户调用的接口 ) ,
适配器类 ( 用户通过调用该类 , 间接调用 被适配者类 ) ;
⑤ 简易原理 :
适配器类 实现用户目标接口 ,
在该接口的实现类中调用被适配者 , 实现了接口转接的效果 ;
使用的时候 , 通过创建适配器类 , 即可间接调用被适配者方法 ;
适配器模式 适用场景 :
1 . 功能正确但接口不匹配 :
对于之前开发好的类 , 该类的操作和返回值都是正确的 , 但是其定义的方法接口无法调用 ,
此时使用适配器模式 , 使该类与用户的接口匹配 , 让用户使用适配器的接口 , 间接调用该类 ;
2 . 适配器模式使用阶段 :
软件设计开发阶段一般不使用适配器模式 , 在软件维护时 , 出现操作和返回值类似 , 但是函数接口不同 , 为了适配第三方系统的接口 , 使用适配器模式 ;
设计阶段不要使用适配器模式 ;
3 . 适配器的两种实现方式 : 对象适配器模式 与 类适配器 ;
① 对象适配器 :
符合组合复用原则 , 使用了委托机制 ; ( 通过组合实现 , 适配器类中维护被适配者成员 )
② 类适配器 :
通过类的继承实现适配器模式 ; ( 通过继承实现 , 适配器类继承被适配者类 )
推荐使用对象适配器模式 , 在继承与组合二者之间 , 优先选择组合方案 ;
1 . 适配器模式 优点 :
① 复用且不修改类 :
不改变现有类的基础上 , 提高类的复用性 , 透明性 ; 让现有类与目标类接口匹配 ;
② 降低耦合 :
目标类 ( 用户调用的接口所在类 ) 和 现有类 ( 被适配者 ) 解除耦合 , 降低了系统的耦合性 , 易于扩展维护 ;
③ 符合开闭原则 :
用户调用适配器接口 , 只与适配器类进行交互 , 如果需要修改扩展 , 只需要修改适配器类即可 , 目标类 和 现有类 各自都是相互独立的 , 互不影响 ;
2 . 适配器模式 优点 :
① 增加复杂性 :
编写适配器类时 , 需要考虑全面 , 包括被适配者 和 目标类 , 系统复杂性会增加 ;
② 降低可读性 :
系统代码可读性降低 , 可维护性降低 ;
阅读代码时 , 调用系统接口 , 如果调用的是适配器接口 , 还要到处找调用的是哪个现有类的实际接口 ;
适配器模式 与 外观模式对比 :
1 . 相同点 :
都是对现有类进行封装 ;
2 . 行为分析 :
① 外观模式行为 :
外观模式定义了新街口 , 处理多个子系统之间的交互 ;
② 适配器模式行为 :
适配器模式复用原有接口 , 只是简单的转接了一下 , 使两个现存接口 ( 现有类 和 目标类 ) 协同工作 ;
3 . 适配力度分析 :
① 外观模式 :
适配力度很大 , 需要开发整个子系统之间的交互流程 ;
② 适配器模式 :
修改很少的内容 , 只是进行简单的接口转接交互 , 一般不实现具体的功能 ;
适配器模式 相关角色 ( 重点 )
1 . 被适配者 :
实际功能提供者 , 是系统中原有的类 ;
2 . 用户目标接口 :
用户调用该接口 , 实现功能操作 ; 是适配器的父类接口 ;
3 . 适配器 :
需要实现 用户目标接口 , 并在接口中的操作中 , 调用被适配者提供的实际功能 ;
适配器有两种途径实现 , 分别是类适配器 , 对象适配器 ;
① 类适配器 :
继承被适配者 , 通过 super 访问被适配者方法 ;
② 对象适配器 ( 推荐 ) :
在适配器中维护一个被适配者成员变量 , 通过成员变量访问被适配者方法 ;
适配器模式 ( 对象适配器 ) 代码实现流程 ( 重点 )
1 . 明确被适配者 :
被适配者 是一个现有类 , 该类保持不变 ;
2 . 定义用户目标接口 :
用户通过调用该接口 , 实现实际的功能 ,
该功能与适配者中的功能类似 , 但 接口不同 ;
3 . 声明适配器 :
① 适配器 实现 用户目标接口 :
适配器 需要实现 用户目标接口 ,
在实现的接口方法中 , 需要将实际操作 委托给 被适配者 ;
② 适配器 维护 被适配者 类型成员变量 :
如何调用到 被适配者 的方法呢 ,
这里 适配器 通过 定义 被适配者 类型的成员变量 ,
通过该 被适配者 类型成员变量 , 调用 被适配者 public 方法 ;
③ 委托操作 :
在实现的 用户目标接口中 ,
通过 被适配者类型 成员变量 , 调用 被适配者 的方法实现具体功能 ;
类适配器
与
对象适配器 ,
本质区别就是 适配器类访问 被适配者的途径 ;
类适配器 :
通过继承 被适配器 , 获取访问被适配器方法的资格 ;
对象适配器 :
通过在其内部维护一个 被适配者 成员变量 , 进而通过该成员变量访问 被适配者方法 ;
4 . 用户访问操作 :
① 定义目标接口变量 :
定义 用户目标接口 对象变量 ;
② 目标接口变量赋值 :
创建 适配器对象 赋值给上述 用户目标接口对象变量 , ( 适配器 是 用户目标接口 的子类 ) ;
③ 目标接口调用 :
调用用户目标接口 , 即可调用被适配者的实际功能方法 ;
2、装饰者模式
【设计模式】装饰者模式 ( 概念 | 适用场景 | 优缺点 | 与继承对比 | 定义流程 | 运行机制 | 案例分析 )
装饰者模式概念 :
① 设计模式类型 :
结构性 ;
② 概念 :
不改变原有类的对象 , 动态地将额外的功能附加到该对象上 ;
③ 扩展对象功能 :
这种功能扩展方式比类继承更加灵活 ;
④ 装饰者模式 :
移除类中的被装饰功能 , 将被装饰类简化 , 区分类的核心职责 和 装饰功能 ;
装饰者模式适用场景 :
① 功能扩展 :
为一个类扩展功能 , 为其添加额外的职责 ;
( 强调扩展 )
② 动态添加撤销功能 :
为一个对象动态添加额外功能 , 同时这些被添加的功能还能被动态撤销 ;
( 强调动态 )
装饰者模式优点 :
① 扩展灵活 :
使用装饰者模式 , 比继承更加灵活 ; 使用装饰者模式扩展类功能 , 不会改变原来的类 ;
② 排列组合 :
对装饰类进行各种排列组合 , 可实现不同的扩展功能 ;
③ 开闭原则 :
装饰者模式符合开闭原则 , 被装饰的类 , 和装饰类相互独立 , 互不干扰 ;
装饰者模式缺点 :
① 程序复杂 :
需要编写更多的代码 , 生成更多的类 , 程序的复杂性增加了 ;
② 动态 / 多层 装饰 :
动态 / 多层 装饰一个类时 , 程序更复杂 ;
3、代理模式
【设计模式】代理模式 ( 简介 | 适用场景 | 优缺点 | 代理扩展 | 相关设计模式 )
【设计模式】代理模式 ( 静态代理 )
【设计模式】代理模式 ( 动态代理 )
代理模式 : 为
其它对象
提供
一种代理
, 以
控制
对
这个对象
的访问 ;
代理对象
在
客户端
和
目标对象
之间 起到
中介的作用 ;
如 : 租客通过中介找房东租房子 , 房东将房子托管给了中介 ,
房东是目标对象
, 但是租赁行为是中介来执行的 ,
中介是代理类
,
租客 就是 客户端 ;
中介
代理
房东
进行租赁行为 , 相当于
代理类对目标对象进行了增强 ;
客户端 通过 代理类 与 目标对象 进行交互 , 客户端 不直接接触 目标对象 ;
代理模式类型 : 结构性 ;
代理模式适用场景 :
保护目标对象 :
客户端
只与
代理类
进行交互 , 不清楚
目标对象
的具体细节 ; 相当于 租客 只与 中介 进行交互 , 不知道房东的信息 ;
增强目标对象 : 代理类 在 目标对象的基础上 ,
对 目标对象的功能 进行增强 ;
代理模式优点 :
分离目标对象 : 代理模式 能将
代理对象
与 真实被调用的
目标对象
分离 ;
降低耦合 : 在一定程度上 ,
降低了系统耦合性
, 扩展性好 ;
保护目标对象 : 代理类 代理目标对象的业务逻辑 ,
客户端
直接与
代理类
进行交互 ,
客户端 与 实际的目标对象之间没有关联 ;
增强目标对象 : 代理类 可以 在 目标对象基础上 ,
添加新的功能 ;
代理模式缺点 :
类个数增加 : 代理模式 会 造成 系统中
类的个数 增加
, 比不使用代理模式增加了代理类 ,
系统的复杂度增加
; ( 所有的设计模式都有这个缺点 )
性能降低 : 在 客户端 和 目标对象 之间 ,
增加了一个代理对象 ,
造成 请求处理速度变慢 ;
静态代理 : 在代码中 ,
使用指定的代理
; 显示的定义了一个业务实现类代理 ; 在代理类中 , 对同名的业务方法进行包装 , 用户通过调用 代理类中 被包装过的业务逻辑方法 , 来调用 被包装对象 的业务方法 , 同时对目标对象的业务方法进行增强 ;
动态代理 : 由 JDK 提供 , 只能对
实现的接口的类
进行动态代理 ,
不能代理具体的实现类
; 通过
接口
中的
方法名
, 在
动态生成的 代理类
中 , 调用
业务实现类
的
同名方法 ;
JDK 动态代理 , 用到的代理类 , 是在程序调
用到代理对象时
,
由 Java 虚拟机创建
, Java 虚拟机 根据传入的
业务实现类对象
以及
方法名
,
动态地创建代理类 Class 文件
,
当该 Class 文件被字节码引擎执行
,
通过该代理类对象进行目标方法的调用 ;
动态代理无法代理类 , 只可以代理接口 ;
CGLib 代理 : 可以
针对类实现进行代理
;
如果要
代理一个类
,
CGLib 会生成一个被代理类的子类
, 通过
继承该类
并
覆盖其中的方法
;
如果该类时 final 的 , 则无法被继承 , 如果类中的方法是 final 的 , 该方法无法被重写 ;
使用 CGLib 代理要特别注意 final 修饰符 ;
4、外观模式
【设计模式】外观模式 ( 概念 | 适用场景 | 优缺点 | 代码示例 )
1 . 外观模式概念 :
① 设计模式类型 :
结构型 ;
② 标准定义 :
提供一个统一接口 , 用于访问子系统中的一群接口 ;
③ 隐藏复杂性目的 :
定义高层级接口 , 让子系统更容易使用 , 目的是隐藏系统的复杂性 ;
④ 交互流程 :
多个子系统联合完成一个操作 , 提供一个统一的接口 , 供客户端调用 , 客户端不与每个子系统进行复杂的交互 , 客户端只与提供接口的外观类进行交互 ;
2 . 外观模式的相关角色 :
① 外观角色 :
外观类有自己的方法 , 用户可以通过调用外观类的方法 , 调用子系统提供的功能 ;
② 子系统角色 :
可以是若干个处理模块 , 数量 1 个或多个 ;
③ 用户角色 :
用户通过外观类调用子系统的功能 ;
外观模式适用场景 :
① 子系统复杂 :
子系统复杂 , 通过使用外观模式可以简化调用接口 ;
② 层次复杂 :
系统结构层次复杂 , 每个层级都一个使用外观对象作为该层入口 , 可以简化层次间的调用接口 ;
5、桥接模式
【设计模式】桥接模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
桥接模式 :
分离抽象实现 : 将
抽象部分
与 它的
具体实现部分
分离 , 使它们 都可以
独立的 变化
; 独立的变化 就是
在一定程度上 进行解耦 ;
组合方式 : 通过
组合
的方式 建立
两个类 之间的联系
, 而
不是 继承 ;
桥接模式类型 : 结构型 ;
桥接模式 相当于
使用桥梁 将两侧连接起来
, 这里指的是 使用桥梁
连接两个类
, 在两个类之间建立某种联系 , 可以通过继承 , 也可以通过组合 , 桥接模式 是采用
组合的方式
, 建立两个类之间的关系 ;
合成复用原则
,
推荐优先使用组合
, 不是继承 ; 桥接模式
可以防止子类过多
, 造成系统复杂的情况 ;
桥接模式的重点 是 理解 类的 抽象部分 和 具体的实现部分 ;
抽象过程 :
抽象部分
, 经过
抽象化
,
忽略某些信息
, 将不同的实体当做同一个对待 ; 面向对象中 ,
将对象的共同性质抽取出来 ,
形成类的过程 ,
就是抽象化过程 ;
实现过程 : 对于具体实现的部分 , 也要进行实现化 ,
针对抽象化
,
给出具体实现
; 这个过程就是实现过程 , 过程的产出就是具体实现部分 , 具体实现部分产生的对象 , 比抽象产生的更具体 , 是对抽象化事物的具体化产物 ;
如 : 开发跨平台的视频播放器 , 平台有 Android , iOS , Windows , Linux , Mac , 播放器支持的格式有 MP4 , AVI , RMVB , FLV 格式 ; 这种情况下 , 适合使用桥接模式 ;
桥接模式适用场景 :
抽象实现灵活 :
抽象
和
具体实现
之间 , 需要
增加更多灵活性
的情况下 , 适合使用桥接模式 ;
使用 桥接模式 , 可以
避免在这两个层次之间
,
建立静态的继承关系
, 通过 桥接模式 在二者之间建立 关联关系 ;
抽象 和 实现 都可以
各自 以继承的方式扩展
,
互不影响 ;
可以动态的 将
抽象 的子类对象
和
实现 的子类对象
进行组合 , 在系统中 ,
抽象 和 实现 之间进行了解耦 ;
独立变化维度 : 一个类存在
2 2 2 个或更多的
独立变化维度
, 并且这些维度都需要
独立扩展 ;
抽象部分可以 独立扩展 , 具体实现的部分 , 也可以独立扩展 ;
不使用继承 : 不希望使用继承 , 或 因多层继承导致系统类的个数增加 ;
6、组合模式
【设计模式】组合模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
组合模式 : 将
对象
组合成
树形结构
, 表示 "
部分-整体
" 层次结构 ;
组合模式 使
客户端
对
单个对象
和
组合对象
保持一致的 方式处理 ;
如 : 文件系统 , 根目录下有若干文件和目录 , 在二级目录下还有目录和文件 , 这种情况下 , 适合使用组合模式 ;
组合模式类型 : 结构型
组合模式适用场景 :
忽略差异 : 希望
客户端
可以 忽略
组合对象
与
单个对象
的差异 ;
处理树形结构 ;
7、享元模式
【设计模式】享元模式 简介 ( 定义 | 对象池 | 内部状态 | 外部状态 | 适用场景 | 相关角色 )
【设计模式】享元模式 实现 ( 实现流程 | 抽象享元类 | 具体享元类 | 享元工厂 | 用户调用 | 代码模板 )
1 . 享元模式 简介 :
享元模式的核心是
对象池 ,
使用对象时 , 先从对象池中获取对象 ,
如果对象池中没有 , 创建一个 , 放入对象池 , 然后再从对象池中获取 ;
( 只能从对象池中拿对象 , 不能自己创建 )
① 设计模式类型 :
结构性 ;
② 享元模式 概念 :
通过减少创建对象数量 , 改善应用中使用对象的结构 , 使用
共享对象 ( 对象池中的对象 )
, 支持多个
细粒度对象 ( 使用时的大量对象 ) ;
③ 好处 :
减少创建对象的数量 , 从而减少内存的占用 , 提高性能 ;
2 . 细粒度对象 和 共享对象 :
目的是为了提高程序性能 ;
① 细粒度对象 :
是内存中的数量庞大的对象 ;
实际使用的数量庞大的对象 ;
② 共享对象 :
多个细粒度对象共享的部分数据 ;
对象缓存池中存储的对象 ;
③ 举例说明 :
使用字符串值 “abc” ,
首次使用 , 创建该字符串 , 将其放入字符串缓存池中 , 这个缓存池中的字符串就是
"共享对象" ,
应用中要大量使用 “abc” 字符串 , 比如使用 10 万个 “abc” 字符串对象 , 这 10 万个字符串对象就是
"细粒度对象" ,
此时肯定不会创建这么多对象 , 这 10 万个对象使用时从字符串缓存池中查找缓存的那个
"共享对象"
即可 , 这样节省了很大的内存开销 ;
3 . 享元模式示例 :
Java 的 String 类型就是用了享元模式的设计模式 ;
① 定义字符串 : String str = "Hello" ;
② 内存中已有该字符串 :
如果之前已经有该字符串 , 就直接将字符串缓存池中的字符串返回 ,
③ 新字符串 :
如果内存中没有该字符串 , 就创建一个新的字符串 , 放入缓存池中 ;
享元模式就是池技术 , 如字符串池 , 数据库连接池 等 ;
使用对象时 , 先从池中查找 , 没有找到再创建该对象 , 然后放入对象池中 ;
4 . 享元模式使用策略 :
用户想要调用一个对象 , 去对象池中查找 , 如果对象池中有该对象 , 那么直接使用该对象 , 如果没有 , 创建该对象 , 放入对象池中 , 然后再从对象池中获取该对象 ;
对象对比 :
这里涉及到一个问题 , 如何确定对象池中的对象是不是用户想要使用的对象呢 ?
5 . 引入 内部状态 和 外部状态 :
对象对比问题引出这两个概念 , 对象中有很多数据 , 那么使用什么数据来确定两个对象是否一致呢 , 这里使用 对象的 外部状态 来确定 ;
① 内部状态 :
对象的内部状态不能作为对象对比的依据 , 所有对象的内部状态都是一样的数据 ;
② 外部状态 :
对象的外部状态每个都不一样 , 每个对象都有一个唯一 外部状态 值 , 类似于 身份证 , 哈希码 这一类的信息 ;
③ 身份标识 :
在线程池中 , 使用外部状态信息 , 唯一确定一个对象 , 作为对象的标识信息 ;
1 . 概念引入 :
区分这两个概念的目的是为了维护享元模式的对象池 , 当用户想要使用某个对象时 , 如何确定对象池中的对象是否是用户想要调用的对象呢 , 这里就需要一些数据进行对比 , 数据一致 , 就说明是用户想要的对象 , 数据不一致 , 就需要创建新对象 , 放入对象池 ;
① 内部状态 :
有些数据所有的对象都一样 , 显然不能当做对象一致性对比的依据 , 这就是 内部状态 ;
② 外部状态 :
有些数据每个对象都不一样 , 根据该数据确定对象的唯一性 , 相当于 哈希码 , 身份证号 , 档案编号 这一类的数据 , 这就是外部状态 ;
内部状态 和 外部状态 本质是 信息数据
2 . 内部状态 ( 共享信息 ) :
在享元模式中的对象中 , 不随环境改变而改变的信息 ;
① 共享信息 :
内部状态就是可以被共享的信息 ;
② 存储位置 :
该信息存储在享元对象内部 ;
③ 存储形式 :
该信息作为对象的附加数据 , 不在具体的对象中存储 , 可以被多个对象共享 ;
3 . 外部状态 ( 不可共享信息 ) :
随着外部环境改变 , 对象内部跟着改变 , 这部分内容就不能进行共享 ;
不可共享 :
外部状态不可被共享 , 每个值都必须在不同的对象中维护 ;
1 . 享元模式 适用场景 :
① 底层开发 :
某个系统的底层开发 , 对性能要求比较高 , 可使用享元模式 ;
② 缓冲池 :
系统中实例对象数量庞大 , 需要缓冲池处理这些对象 ;
2 . 享元模式使用前提 :
系统中存在大量的对象 , 这些对象状态大部分功能可以外部化 , 将这些功能抽离出来 , 只在内存中保留一份 ;
① 分离对象功能 :
系统中如果内存中持有大量对象 , 可能会溢出 , 将这些对象相同的部分分离出来 ;
② 用户调用行为 :
如果有相同的业务请求 , 则优先使用内存中已有的对象进行处理 , 避免使用大量相同的对象 ;
③ 注意 :
这里只有在内存中有大量相同对象时 , 才考虑享元模式 , 如果内存中某类型的对象数量较少 , 没有必要使用该模式 ;
四、行为型模式
1、策略模式
【设计模式】策略模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
策略模式 : 定义了
算法家族
, 分别
封装起来
, 让它们之间 , 可以
相互替换
, 此模式 让
算法的变化
不会影响到 使用算法的用户 ;
将
不同的算法
, 封装到
不同的类
中 , 让它们之间可以
相互替换
,
使用算法的用户 即 应用层 , 感知不到 算法已经被替换了 ;
实际的业务场景 :
不同业务逻辑 : 商品促销 , 促销策略 , 不同的促销策略算法 , 封装到不同的类中 ;
代码优化 : 如果代码中 , 有大量的 if … else … 代码 , 可以通过策略模式 , 替换相关逻辑 ;
策略模式类型 : 行为型 ;
策略模式适用场景 :
行为切换 : 系统有
很多类
, 这些类的区别仅仅在于它们的
行为不同
; 使用策略模式 , 可以
动态地
让
用户对象
在这些行为中,
选择一个行为
;
将对象的
不同的行为
, 封装到
不同的类
中 ,
每个行为对应一种策略 ;
算法选择 : 系统中需要
动态地
在
几种算法
中
选择一种
;
算法 就是 策略 ,
其中封装了一系列的业务逻辑及计算方式 ;
如 : 计算方式 , 给定两个数字 ; 使用加法策略 , 将两个数相加 ; 使用乘法策略 , 将两个数相乘 ;
2、观察者模式
【设计模式】观察者模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
观察者模式 : 定义了
对象之间
一对多
的 依赖 , 令 多个
观察者
对象 同时 监听 某一个
主题对象
, 当
主题对象
发生改变时 , 所有的
观察者
都会
收到通知 并更新 ;
观察者
有多个 , 被观察的
主题对象
只有一个 ;
如 : 在购物网站 , 多个用户关注某商品后 , 当商品降价时 , 就会自动通知关注该商品的用户 ;
主题对象 : 商品是主题对象 ;
观察者 : 用户是观察者 ;
观察者注册 : 用户关注 , 相当于注册观察者 ;
通知触发条件 : 商品降价 ;
观察者模式 类型 : 行为型 ;
观察者模式适用场景 :
关联行为
场景 , 建立一套
触发机制 ;
如 : 用户关注某个商品的价格 , 降价时进行通知 , 这样
用户
和
商品
产生了关联 , 触发机制就是
商品降价 ,
3、责任链模式
【设计模式】责任链模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
责任链模式
, 又称为
职责链模式 ;
责任链模式定义 : 为
请求
创建一个接收该
请求对象
的
链
, 链条中每个元素都是一个对象 ;
责任链模式类型 :
行为型 ;
责任链模式 适用场景 : 一个
请求
的
处理
, 需
要多个对象
中的
一个或若干个对象
协作进行处理 ;
责任链模式 优点 :
① 解耦 : 请求的
发送者
和
接收者
解耦 ; 接收者 是
请求的处理者 ;
② 动态组合 : 责任链 可以
动态组合
, 使用配置 设置责任链的
顺序
及
是否出现
; 可以随时对责任链排序 , 随时增加拆除责任链中的某个请求对象 ;
责任链模式 缺点 :
① 性能 : 如果 责任链
太长
, 或责任链中请求的
处理时间过长
, 可能会
影响性能 ;
② 个数 : 责任链
可能过多 ;
责任链模式 与 状态模式 比较 :
在
责任链模式
中 , 并
不指定
下一个处理的 请求对象 是哪个
; 责任链 中 链条顺序 可以
任意组合排序 ;
在
状态模式
中 , 需要让
每个 状态
知道下一个需要处理的状态是谁 ;
4、备忘录模式
【设计模式】备忘录模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
备忘录模式 : 保存
对象
的
某个状态
, 以便在 适当的时候
恢复对象
;
( 形象的比喻 : " 后悔药 " )
如 : 游戏存档 , 一些编辑工具中的 "
撤销
" 操作 , 浏览器中的
后退 ;
备忘录模式 类型 : 行为型 ;
备忘录模式 适用场景 :
撤销操作 :
保存 / 恢复 数据
的相关业务场景 ;
如 : 在 Word 中编写文档 , 如果想要撤销之前的 输入 / 删除操作 , 使用 Ctrl + Z 执行 " 撤销 " 操作 ;
状态恢复 : 在 " 后悔 " 的时候 ,
将对象恢复到之前的状态 ;
如 : 游戏中的存档使用 ;
5、模板方法模式
【设计模式】模板方法模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
模板方法模式 : 定义了一个
算法
的
骨架
, 并允许 子类 为 一个或多个 步骤
提供实现 ;
模板方法模式 可以使
子类
在不改变
算法结构
的前提下 ,
重新定义算法的某些步骤 ;
模板方法模式类型 : 行为型 ;
模板方法模式适用场景 :
父类视角 : 一次性 实现 一个算法
不变的部分
, 并将
可变部分
留给
子类 实现 ;
子类视角 : 各个子类中 ,
公共部分 被提取出来 ,
集中到一个公共的父类中 ,
避免代码重复 ;
模板方法模式的目的是 让
子类可以扩展
或
具体实现固定方法的某个具体的步骤
; 对于模板来说 , 是一套固定的算法 , 通过子类 可以扩展 固定算法中某些算法步骤 ;
6、迭代器模式
【设计模式】迭代器模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
迭代器模式 : 提供一种方法 ,
顺序访问
集合对象
中的
各个元素
, 而
不暴露 该对象 的内部表示 ;
迭代器模式类型 : 行为型 ;
迭代器模式适用场景 :
内容保密 : 访问
集合对象
的内容 ,
无需暴露内部表示 ;
统一接口 : 为遍历 不同的 集合结构 ,
提供统一接口 ;
迭代器模式优点 :
分离
了 集合对象 的
遍历行为
; 抽象出了
迭代器
负责
集合对象的遍历
, 可以让外部的代码
透明的
访问集合内部的数据 ;
迭代器模式缺点 :
类的个数成对增加
; 迭代器模式 , 将
存储数据
,
遍历数据
两个职责拆分 ; 如果新添加一个
集合类
, 需要增加该 集合类 对应的
迭代器类
, 类的个数成对增加 , 在一定程度上 ,
增加了系统复杂性 ;
7、中介者模式
【设计模式】中介者模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
中介者模式 : 定义 一个
封装一组对象
如何
交互
的
对象 ;
通过使
对象
明确地
相互引用
, 促进
松散耦合
, 允许
独立改变
它们之间的
交互 ;
中介者模式类型 : 行为型 ;
中介者模式适用场景 :
引用关系复杂 : 系统中
对象之间
存在
复杂的 引用关系
, 产生的
相互依赖关系
结构混乱
,
难以理解 ;
改变行为 : 交互的
公共行为
, 如果 需要
改变行为
, 可以
增加新的 中介者 类 ;
( 通过增加新的中介者类 , 达到扩展的目的 )
多人聊天室 就是一个 中介者模式 场景 , 一个人发言时 , 需要传达给每个人 , 如果没有聊天室 , 需要对每个人都说一遍 , 如果有中介者 , 就由中介者负责将发言传达给每个人 ;
中介者模式优点 :
降低复杂度 : 将
一对多
转化为
一对一
,
降低了 程序复杂程度 ;
如 : 聊天室中有 8 8 8 个人 , 如果要一对一进行交互 , 需要交互 7 7 7 次 ; 使用了中介者模式后 , 变成一对一 , 只要将交互内容交给中介者就可以了 , 中介者负责与其余 7 7 7 人进行交互 ;
解耦 : 实现了
类之间的解耦 操作 ;
如 : 聊天室中有 8 8 8 个人 , 每个人都需要耦合另外 7 7 7 个 , 即持有另外 7 7 7 个对象 , 使用了中介者模式之后 , 8 8 8 个人只需要持有 中介者 对象即可 , 8 8 8 个人之间不再进行相互耦合 ;
中介者模式缺点 : 如果在 业务场景 中
中介者 数量过多
, 会导致系统
复杂性增加 ;
( 设计模式之间 , 也是一个相互平衡的过程 )
8、命令模式
【设计模式】命令模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
命令模式 : 将
不同的请求
封装成
不同的请求对象
, 以便
使用 不同的 请求
; 对于接收者来说 , 可以识别 不同的 请求对象类型 , 然后执行 不同的操作 ;
命令模式 , 解决了 应用程序 中 ,
对象的职责
(
发送请求
/
执行请求
) , 以及它们之间的
通信方式 ;
命令模式 可以使 命令的
发送者
和
接收者
完全解耦
; 发送者 和 接收者 之间 , 并没有直接的关系 , 二者靠
命令
进行交互 ;
命令发送者
只需要知道发送 请求对象 , 不需要知道如何完成请求 ;
命令执行者
只需要知道如何 完成请求 , 不需要知道请求的发送过程 ;
命令模式类型 : 行为型 ;
命令模式 适用场景 :
解耦发送者与接收者 :
请求发送者
和
请求接收者 ( 执行者 )
需要
解耦
, 发送者 与 接收者 之间
不直接进行交互 ;
抽象行为 : 需要将
等待执行
的行为
抽象出来 ;
9、访问者模式
【设计模式】访问者模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
访问者模式 :
封装
作用于 某种 数据结构 的 各元素
操作
, 数据结构指的是 List , Set , Map 等 ;
在
不改变 元素类
的前提下 , 定义
作用于 元素 的操作 ;
访问者模式类型 :
行为型 ;
访问者模式 适用场景 :
List , Set , Map 等 数据结构 中 , 包含
很多类型的对象 ;
数据结构 与 数据操作
分离 ;
10、解释器模式
【设计模式】解释器模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
解释器模式 : 给定一个
语言
, 定义它的
文法
的一种表示 , 并定义一个
解释器
, 这个 解释器 使用该表示来
解释 语言中的 句子 ;
文法
可以理解成一种
语法 ;
为了解释一种语言 , 而为语言创建的
解释器 ;
如 : Java 代码 , 需要编译器进行编译之后才能运行 , 这个编译器就相当于解释器 ;
解释器模式类型 : 行为型 ;
解释器模式适用场景 : 某个
特定类型问题
发生频率
足够高 ;
日志处理 : 使用 脚本语言 或 编程语言 处理日志时 , 有很多服务 会产生 大量的日志 , 需要
对日志进行解析 , 生成报表 ;
各个服务的日志格式不同 , 数据中的要素相同 , 这种情况下 , 通过程序解决上述问题 , 主要的解决方案就是使用解释器模式 ;
日常项目中 , 解释器模式使用情况很少 ;
解释器一般是 开源包 , 如 Express4J , JEP ;
11、状态模式
【设计模式】状态模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
状态模式 : 允许 对象 在
内部状态 改变时 ,
改变它的行为 ;
一个对象 , 如果其
内部状态改变
, 其
行为也需要进行改变
; 如果其行为不需要改变 , 也可以只
控制 该对象的状态 的 互相转换 ;
当控制一个对象 , 其状态转换过程比较复杂时 , 将
状态判断逻辑 ,
转到代表不同状态的一系列类中 ;
如 : 引入 视频播放 的业务场景 , 播放器有 初始状态 , 播放状态 , 暂停状态 , 停止状态 , 快进状态 等多种状态 , 将这些
状态
都封装到
代表不同状态的类
中 , 可以将复杂的判断逻辑简化 , 将这些
逻辑
扩展到不同的状态类中 ;
状态模式类型 : 行为型 ;
状态模式适用场景 : 一个对象 ,
存在多个状态 ,
状态可以相互转换 ;
不同状态下 ,
行为不同 ;
不同状态下 , 行为不同的示例 , 如 :
购买物品 , 将物品放入购物车并生成订单 , 可以进行付款 ; 如果 订单 超过 24 小时后 , 被关闭订单 , 此时订单取消 , 无法付款 ;
电梯运行时 , 不能开门 ; 电梯停止后 , 才能开门 ;
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。