dcddc

西米大人的博客

0%

系统学习设计模式

前言

设计模式包含三大类:创建模式、行为模型、结构模式

创建模式

  • 专注于类的实例化,简化新实体的创建过程,例如简单工厂、单例(Singleton)和抽象工厂(Abstract Factory)

行为模型

  • 将组件之间的通用行为抽象为一个单独的实体,进而与你的创建模式结合起来。常见的行为模式包括命令(Command)、策略(Strategy)、观察者(Observer)模式

结构模式

  • 负责处理不同组件(或类)之间的关系,并形成新结构以提供新功能。常用的结构模式有组合(Composite)、适配器(Adapter)和装饰器(Decorator)

创建模式

工厂模式

遵循工厂模式的概念,在实际应用中衍生出了几种设计模式,分别是简单工厂、工厂包、抽象工厂

简单工厂

  • 由工厂类负责按需创建同一类产品(实现了相同接口的对象实例)
  • 缺点:增加新类型的产品时,需要修改工厂类的工厂方法
  • 解决办法:抽象”工厂方法”得到抽象工厂,每个类型的产品都有自己独立的工厂实例,负责创建产品实例

抽象工厂

  • 将创建产品实例的工厂方法抽象为接口,得到抽象工厂,由抽象工厂的实现类负责创建产品实例,每个类型的产品都有自己的抽象工厂实例
  • 由工厂制造器负责按需创建特定的工厂实例

抽象工厂的应用场景:

  • 工厂无法预期它必须创建的产品实例类型
  • 希望由不同的工厂分别创建自己领域下的产品实例

工厂包

  • 使用 Builder 构建(不可更改的)产品实例的方式定义工厂
  • Builder 与工厂是分离的,由业务方使用 Builder 构建工厂的产品实例
  • 实现方案使用 JAVA8 的函数式接口:Consumer、Supplier
    • Consumer 是消费者,专注于处理 Consumer 的泛型入参
    • 使用 Builder 接口作为 Consumer 的泛型入参,业务方实现 Consumer,调用 Builder 的 add 方法向工厂添加产品实例
    • 工厂提供 Builder 的实现类,获取业务方提供的产品实例并添加到工厂,简单的实现方式就是一个 Map
    • Supplier 是提供者,专注于提供产品
    • Builder 的接口方法参数使用 Supplier,业务方在调用 Builder 的 add 方法时,实现 Supplier,自定义产品实例的创建逻辑

工厂包的应用场景:

  • 工厂无法预期它必须创建的产品实例类型
  • 业务方自定义 Builder 来灵活构建自适应工厂,而非依赖全局工厂
  • 明确知道工厂需要产出的产品实例类型

单例模式

单例模式保证了类在每个类加载器实例中只会有一个类实例对象,并提供了全局方法来访问这个单例对象

单例模式的几种实现:

  • 饿汉式
    • 单例实例作为类属,在类加载时被实例化
  • 简单懒汉式
    • 单例实例作为类属,在第一次被访问时加锁实例化
  • DCL 懒汉式
    • 单例实例作为类属并用 volatile 修饰,在第一次被访问时先利用可见性判断是否已实例化,如果没有才加锁,加锁后再二次判断是否已实例化,没有再实例化。适合高并发场景 - 加 volatile 关键字保证可见性,防止指令重排序在并发时出现问题,导致没有初始化的单例对象被业务线程拿到
  • 静态内部类的懒汉式
    • 单例实例作为静态内部类的类属,在第一次被访问时通过加载静态内部类完成实例化,而静态内部类只有在被访问时加载,所以也是 lazy 方式,且是线程安全的

builder 模式

建造者模式提供一套复杂对象的通用构造流程,优雅地创建同一类对象的不同呈现

builder 模式的实现:

  • target 类里创建一个静态类 Builder,属性是需要通过 build 模式构建的属性
  • builder 提供方法为 builder 实例填充属性
  • builder 提供 build 方法,内部调用 target 的构造器创建 target 实例,传入 builder 实例完成属性传递

行为模式

command 模式

命令模式将面向对象里执行命令的函数对象化为执行者 Command,Command 只负责执行命令逻辑,不关心目标对象 Target 是谁,而 Target 也不知道被什么 Command 执行命令。业务方 Client 使用 Invoker 来发号施令,提供给 Invoker 实际的执行者和目标,Invoker 发号施令的同时还提供命令的撤回和重做能力

命令模式:

  • 命令模式包含四类元素:Command、Target、Invoker、Client
  • 执行者 Command 专注于执行对目标 Target 的命令,而不关心 Target 具体是谁
  • Invoker 负责发号施令,不关心实际的执行者 Command 和目标 Target 是谁
    • Invoker 内部通过维护执行者 Command 的 undo 和 redo 队列,回调 command 的 undo/redo 方法实现命令的撤销和重做
  • Client 操作 Invoker 来发号施令,指定实际的执行者 Command 和目标 Target

Command 模式的应用场景

  • 回调函数建模
    • 回调函数对象化为命令 Command
  • 多级回退 Undo 操作
  • 事务建模
    • 事务建模为 Command,当事务失败时,回退到事务前的状态
    • Command 具有公共接口,可让您以相同的方式调用所有事务

observer 模式

定义被依赖方和依赖方之间一对多的关联关系,当被依赖方的状态改变时,可以通知多个依赖方自动更新状态

观察者模式

  • 三个核心元素:观察者 Observer、被观察者 Observable、回调上下文 Argument
  • 被观察者 Observable 维护一组观察者 Observer,提供基本的添加和删除观察者能力,以及 notify 能力,通知它的所有观察者,回调观察者的 update 接口消费事件
  • Argument 是被观察者 Observable 在执行事件后用于回调所有观察者的上下文参数
  • Observable 是观察者模式的顶级抽象类,Observer 是顶级接口。它们都适合使用泛型,由业务方继承时指定实际的 Observable、Observer、Argument

使用场景

  • 当一个对象改变需要通知其他对象,且你无法确定其他对象的个数时
  • 当你需要将依赖方和被依赖方解耦,被依赖方不需要感知有哪些依赖方
    • 目的是依赖方和被依赖方作为独立模块能分别复用和修改

strategy 模式

策略模式提供一组策略,帮助业务在运行时动态选择和执行决策,使得业务行为具备动态性

策略模式的实现:

  • 定义顶层 Strategy 策略接口,并提供一组策略的实现类
  • 提供策略上下文类,实现方式可以是简单工厂,或者是枚举,帮助业务在运行时动态选择策略
    • 枚举方式代码可读性更好,更符合开闭原则
  • 业务类依赖策略接口,运行时动态注入策略实例,执行不同的策略有不同的行为表现

使用场景:

  • 许多相似的类仅仅在行为上有不同的表现,可以使用策略模式统一这些相似的类
  • 类的行为包含很多 if-else 条件,可以将类的行为抽象为策略,相关的条件分支移到策略类里实现

结构模式

Composite 模式

组合模式将不同组件按树状结构组合在一起,表示部分和整体的层级结构。作用是使得业务方只需统一处理父级组件,就能达到处理整个层级结构的效果

组件间需要满足:

  • 组件的类型相同,即组件需要有共同行为,实现同一接口
  • 组件间的结构关系是树形结构

举例:

  • 一句话、一个词、一个字都是相同类型的组件,因为这些组件都有相同的打印行为,且这些组件间满足树形结构,一句话包含多个词,一个词包含多个字。所以在打印这些组件时,可以使用组合模式
  • 商品、购物车也是相同类型的组件,都具备计算价格的行为,且 Box 是父节点,Product 是叶子节点。所以在计算这类组件的价格时,可以使用组合模式

应用场景:

  • 你想要构建”部分-整体”的对象间层级结构
  • 你想要业务方无需关心父级对象和它内部的组成结构,只需要统一处理父级对象

decorator 模式

装饰者模式能实现动态地将额外功能附加到对象,不需要通过子类来实现,灵活性更强

装饰者模式包含的元素有装饰者 decorator 和被装饰者 decorated

装饰者 decorator 需要满足:

  • 与被装饰者 decorated 实现同一接口,否则不能装饰
    • 这里装饰就像是被代理
  • 聚合被装饰者 decorated 的实例

应用场景:

  • 动态透明地为对象附加功能
    • 动态体现在:
      • 运行时额外增加功能而非通过定义子类在类加载时就已经扩展功能
      • 如果不装饰就能做到撤销功能
      • 装饰者也可以作为被装饰者来二次装饰,功能扩展更为灵活
    • 透明体现在不影响对象,既不改变对象内部代码也不用扩展对象类型的子类
  • 当使用子类扩展功能不可行时
    • 对象不支持扩展子类
    • 避免扩展太多作用不大的子类

adapter 模式

适配器模式的作用是适配业务方期望的接口类型,实现在接口不兼容时也能协同工作

Adapter 模式包含两个元素:Adapter 和 Adaptee

  • Adapter 是适配器,Adaptee 是被适配者
  • Adapter 通过注入 Adaptee 来复用 Adaptee 的能力
  • Adapter 实现业务方期望的接口来达到适配的效果

使用场景:

  • 您要使用现有的类,并且其接口与您需要的接口不匹配
  • 使用第三方类库时,使用适配器作为中间层使应用程序与三方类库分离

bridge 模式

桥接模式可以认为是两层抽象,业务方可以使用桥接模式将两种类型的实现类自由结合起来实现功能,而不是创建深层次的等级体系

桥接模式通过定义两个顶级接口,并将其中一个接口注入另一个中,使得在接口级别抽象为上下游两层,上层接口依赖下层接口实现功能(桥接的含义)。业务方可以自由组合这两种类型的实现类,灵活替换接口实现类满足功能需求变更

使用场景:

  • 运行时动态选择和切换实现类