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

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

文章目录

一、七大设计原则

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 22 个职责 , 职责 1 11 和 职责 2 22 ;

一旦 需求发生变更 , 如 职责 1 11 相关功能发生改变 ;

修改该类的 职责 1 11 功能时 , 有可能导致原本运行正常的职责 2 22 发生故障 ;


对于上述类 , 应该 分别针对 职责 1 11 和 职责 2 22 , 各自建立一个独立的类 , 这样就保证了系统的稳定性 ;

这样修改 职责 1 11 和 职责 2 22 中的任何一个功能 , 都不会影响另外一个职责的功能 ;



推荐的开发方法 : 使一个 类 / 接口 / 方法 只负责一项职责 ;



单一职责优点 : 提高 类的 可读性 , 提高 系统的 可维护性 , 降低 类的复杂度 , 降低 变更引起的风险 ;


类越简单 , 可读性越好 , 同时提高了可维护性 ;

一个类只负责一个职责 , 比负责多个职责 , 类要 简单得多 ;

变更是必然的 , 必须要接收变更 , 如果 单一职责原则遵守的好 , 当修改一个功能时 , 可以 显著降低对其它功能的影响 ;



单一职责原则 不只是 面向对象 设计中特有的职责 , 只要是模块化的系统 , 都适合使用单一职责原则 ;



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 77 大设计原则 , 不能全部遵守 , 也不能不遵守 , 注意平衡 功能 和 系统复杂度 , 找到最合适的一个点 ;



1、工厂方法模式


【设计模式】工厂方法模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )



工厂方法模式 : 定义一个 创建对象 的 接口 , 让 实现这个接口的子类 决定 实例化哪个类 , 工厂方法让 类的实例化 推迟到子类中进行 ;


工厂方法模式类型 : 创建型 ;



创建 实例对象 过程可能会很复杂 , 有可能会 导致大量的重复代码 , 工厂方法模式 通过 定义 一个单独的 创建 实例对象 的方法 , 解决上述问题 ;

通过 子类 实现 这个方法 , 创建具体的 实例对象 ;



工厂方法模式适用场景 :


重复代码 : 创建对象 需要使用 大量重复的代码 ;

不关心创建过程 : 客户端 不依赖 产品类 , 不关心 实例 如何被创建 , 实现等细节 ;

创建对象 : 一个类 通过其 子类 来 指定 创建哪个对象 ;


客户端 不需要知道 具体 产品类 的 类名 , 只需要知道 所对应的工厂 即可 , 具体的产品对象 , 由对应的工厂创建 , 客户端只需要知道 产品 对应的 工厂 ;

工厂方法模式 利用了 面向对象 的 多态性 , 和 里式替换 原则 ;

子类对象 覆盖 父类对象 , 使 系统 更容易扩展 , 将 创建对象的过程 推迟到子类实现 , 创建对象的任务 , 委托给 多个 工厂子类 中的某一个 , 客户端不需要关心是哪个 工厂子类 创建的 产品对象 ;

工厂子类 一般都是 需要的时候 , 动态指定 ;



工厂方法模式优点 :


不关心创建细节 : 用户 只需要 关心 所需产品 对应的工厂 , 无需关心创建细节 ;

符合开闭原则 : 加入 新产品 , 符合开闭原则 , 提高可扩展性 ;

工厂方法模式 中 , 使用 工厂类创建 产品对象 , 同时 隐藏了 具体的 产品类 被 实例化 的细节 ;



工厂方法模式缺点 :


增加复杂性 : 类的个数容易过多 , 增加系统复杂度 ;

在 添加新产品 时 , 除了编写 新的产品类 之外 , 还要 编写该产品类对应的 工厂类 ;

增加难度 : 增加了系统 抽象性 和 理解难度 ;

工厂方法本身 利用了抽象 , 该模式中会 引入抽象层 , 如果要动态创建产品类 , 还要 引入反射技术 ;

设计模式 的 使用 , 要根据 实际的 业务场景 , 模型 综合平衡考量 , 不能过分遵守设计原则 和 设计模式 ;



2、抽象工厂模式


【设计模式】抽象工厂模式 ( 简介 | 适用场景 | 优缺点 | 产品等级结构和产品族 | 代码示例 )



抽象工厂模式 : 提供 一个 创建 一系列 相关 或 相互依赖 对象 的接口 ;


创建目标对象时 , 只需要直到对象的抽象类型或接口类型即可 , 不需要知道具体的实现类型 ;


抽象工厂模式类型 : 创建型 ;



抽象工厂模式 可以将 一组具有同一主题 , 单独的工厂 封装起来 ;

在使用时 , 客户端 创建 抽象工厂 的实现 , 使用 抽象工厂 作为接口 , 来创建这一主题的对象 ;

使用的时候 , 不需要知道 从内部 工厂方法中获得的 对象 的具体类型 ;

客户端 只 使用这些对象的 通用接口 ;


抽象工厂模式 实现了 一组对象的 实现细节 与 使用 分离 ;



抽象工厂模式适用场景 :


忽略创建细节 : 客户端 不关心 产品实例 如何 被创建 , 实现等细节 ;

创建产品族 : 强调 一系列 相关的 产品对象 , 一般是 同一个产品族 , 一起使用 创建对象需要大量重复的代码 ;

产品类库 : 提供 一个 产品类 的库 , 所有的产品 以 同样的接口出现 , 使 客户端不依赖于具体实现 ;

使用抽象工厂模式 , 可以在工厂变化时 , 不需要修改 客户端 使用工厂的 代码 ;



抽象工厂模式优点 :


隔离产品代码 : 在 应用层 隔离 具体产品的代码 , 客户端 无须关心 产品创建 的细节 ;

创建产品族 : 将 一个系列 的 产品族 , 统一到一起创建 ;


抽象工厂模式缺点 :


扩展困难 : 规定了 所有 可能 被创建 的 产品集合 , 产品族 中 扩展 新的产品 困难 , 需要 修改抽象工厂的接口 ;

增加难度 : 增加了系统的 抽象性 和 理解难度 ;



产品等级结构和产品族 :


下图中 , 有 椭圆形 , 圆形 , 正方形 , 三种产品 ;


产品族 : 相同颜色的代表一个产品族 ;

产品等级结构 : 相同形状的代表同一个产品等级结构 ;



如 : 方型 - 洗衣机 , 圆形 - 空调 , 椭圆 - 冰箱 ;


横向 看 产品族 : 某 品牌下 有 方型 - 洗衣机 , 圆形 - 空调 , 椭圆 - 冰箱 , 这是一个产品族 ;

纵向看产品等级结构 : 椭圆 - 冰箱 , 纵向指的是不同品牌的冰箱 ;


工厂方法模式 针对的是 产品等级结构 , 可以 扩展多个相同的产品 ;


抽象工厂模式 针对的是 产品族 , 在 某个品牌 的工厂中取出 洗衣机 类 , 取出的肯定是 该品牌的洗衣机实例对象 ;


只要指出 产品所在的 产品族 以及 产品所在的 产品等级结构 , 就可以唯一确定一个产品 ;



左侧的 纵坐标轴 上的 工厂 是 具体的工厂 , 从该具体的工厂中 创建的实例 是 该产品族中的实例 ;





使用 工厂模式 还是 抽象工厂模式 , 要看具体的业务场景 ;

当一个工厂 可以 创建 分属于 不同 产品等级结构 的 一个 产品族 中的 不同对象时 , 使用 抽象工厂模式 ;


如 :


工厂 中可以创建 相同品牌的 洗衣机 , 冰箱 , 空调 等产品 , 使用 抽象工厂模式 ;

如果 工厂中创建 不同品牌的空调 , 则使用 工厂方法模式 ;


目录
相关文章
|
3月前
|
设计模式 前端开发 算法
【面试题】 ES6 类聊 JavaScript 设计模式之行为型模式(二)
【面试题】 ES6 类聊 JavaScript 设计模式之行为型模式(二)
|
1月前
|
设计模式 存储 安全
Java设计模式---结构型模式
Java设计模式---结构型模式
|
3月前
|
设计模式 算法
设计模式 - 行为型模式_ 访问者模式Visitor Pattern
设计模式 - 行为型模式_ 访问者模式Visitor Pattern
39 1
设计模式 - 行为型模式_ 访问者模式Visitor Pattern
|
3月前
|
设计模式 Java 应用服务中间件
设计模式 -结构型模式_门面模式(外观模式) Facade Pattern 在开源软件中的应用
设计模式 -结构型模式_门面模式(外观模式) Facade Pattern 在开源软件中的应用
30 1
|
3月前
|
设计模式 缓存 安全
设计模式 - 创建型模式_ 单例模式 Singleton Pattern
设计模式 - 创建型模式_ 单例模式 Singleton Pattern
39 0
|
1月前
|
设计模式 存储 算法
Java设计模式---行为型模式
Java设计模式---行为型模式
|
1月前
|
设计模式 算法 安全
Java设计模式---创建型模式
Java设计模式---创建型模式
|
2月前
|
设计模式 存储 安全
【设计模式】创建型模式之单例模式(Golang实现)
【2月更文挑战第3天】一个类只允许创建一个对象或实例,而且自行实例化并向整个系统提供该实例,这个类就是一个单例类,它提供全局访问的方法。这种设计模式叫单例设计模式,简称单例模式。
34 1
|
3月前
|
设计模式 安全 Java
Java 设计模式最佳实践:二、创建型模式
Java 设计模式最佳实践:二、创建型模式
|
3月前
|
设计模式 关系型数据库 程序员
【设计模式】设计原则
【1月更文挑战第12天】【设计模式】设计原则