【设计模式】设计模式总结 ( 七大设计原则 | 创建型模式 | 结构型模式 | 行为型模式 ) ★★★

网友投稿 662 2022-05-29

文章目录

一、七大设计原则

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小时内删除侵权内容。

上一篇:Kubernetes — 调度系统
下一篇:FPGA基础知识极简教程(6)UART通信与移位寄存器的应用
相关文章