设计模式学习整理

gq-z / 2024-10-14 / 原文

  • 学习参考:重构大师

创建型模式

  • 创建型模式提供了创建对象的机制, 能够提升已有代码的灵活性和可复用性。

工厂方法模式

  • 在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型;
  • 实现方式:使用特殊的工厂方法代替对于对象构造函数的直接调用 (即使用 new运算符), 对象仍将通过 new运算符创建, 只是该运算符改在工厂方法中调用罢了。 工厂方法返回的对象通常被称作 “产品”。

抽象工厂模式

  • 创建一系列相关的对象, 而无需指定其具体类;
  • 实现方式:
    • 对于系列产品的每个变体, 都将基于抽象工厂接口创建不同的工厂类,每个工厂类都只能返回特定类别的产品;
    • 客户端代码可以通过相应的抽象接口调用工厂和产品类。 你无需修改实际客户端代码, 就能更改传递给客户端的工厂类, 也能更改客户端代码接收的产品变体。

生成器模式

  • 能够分步骤创建复杂对象, 该模式允许你使用相同的创建代码生成不同类型和形式的对象;
  • 应用场景:对于一个复杂对象, 在对其进行构造时需要对诸多成员变量和嵌套对象进行繁复的初始化工作,通常情况下, 绝大部分的参数都没有使用, 这使得对于构造函数的调用十分不简洁(如大部分参数不适用传入NULL等);
  • 实现方式
    • 将对象构造代码从产品类中抽取出来, 并将其放在一个名为生成器的独立对象中;
    • 每次创建对象时, 你都需要通过生成器对象执行一系列步骤。 重点在于你无需调用所有步骤, 而只需调用创建特定对象配置所需的那些步骤即可;
    • 可以进一步将用于创建产品的一系列生成器步骤调用抽取成为单独的主管类。 主管类可定义创建步骤的执行顺序, 而生成器则提供这些步骤的实现。

原型模式

  • 使使用者能够复制已有对象, 而又无需使代码依赖它们所属的类;
  • 实现方法:将克隆过程委派给被克隆的实际对象;即所有支持克隆的对象声明了一个通用接口, 该接口让你能够克隆对象, 同时又无需将代码和对象所属类耦合。 通常情况下, 这样的接口中仅包含一个克隆方法。

单例模式

  • 保证一个类只有一个实例, 并提供一个访问该实例的全局节点;
  • 实现方式:将默认构造函数设为私有;新建一个静态构建方法作为构造函数供外部调用,其中调用私有构造函数来创建对象, 并将其保存在一个静态成员变量中,对该函数当调用都将返回缓存的对象;

结构型模式

  • 结构型模式介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。

适配器模式

  • 意图:适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作。
  • 解决方案:
    • 适配器实现与其中一个现有对象兼容的接口;
    • 现有对象可以使用该接口安全地调用适配器方法;
    • 适配器方法被调用后将以另一个对象兼容的格式和顺序将请求传递给该对象。

桥接模式

  • 意图:桥接模式是一种结构型设计模式, 可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构, 从而能在开发时分别使用。
  • 解决方案:
    • 对于有多个不同维度的类扩展时,每新增一个类别考虑其他维度都会导致子类指数增加;
    • 抽取其中一个维度并使之成为独立的类层次, 这样就可以在初始类中引用这个新层次的对象, 从而使得一个类不必拥有所有的状态和行为;
      抽象部分与实现部分:
    • 抽象部分 (也被称为接口) 是一些实体的高阶控制层。 该层自身不完成任何具体的工作, 它需要将工作委派给实现部分层 (也被称为平台)。

组合模式

  • 意图:组合模式是一种结构型设计模式, 你可以使用它将对象组合成树状结构, 并且能像使用独立对象一样使用它们。
  • 结构图:
  • 实现方式:
    • 1、确保应用的核心模型能够以树状结构表示。 尝试将其分解为简单元素和容器。 记住, 容器必须能够同时包含简单元素其他容器
    • 2、声明组件接口及其一系列方法, 这些方法对简单和复杂元素都有意义。
    • 3、创建一个叶节点类表示简单元素。 程序中可以有多个不同的叶节点类。
    • 4、创建一个容器类表示复杂元素。 在该类中, 创建一个数组成员变量来存储对于其子元素的引用。 该数组必须能够同时保存叶节点和容器, 因此请确保将其声明为组合接口类型。
      • 实现组件接口方法时, 记住容器应该将大部分工作交给其子元素来完成。
    • 5、最后, 在容器中定义添加和删除子元素的方法。

装饰模式-- 装饰者模式、装饰器模式、Wrapper、Decorator

  • 意图:允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。
  • 聚合或组合概念:一个对象包含指向另一个对象的引用, 并将部分工作委派给引用对象;
  • 封装器:一个能与其他 “目标” 对象连接的对象。 封装器包含与目标对象相同的一系列方法, 它会将所有接收到的请求委派给目标对象。 但是, 封装器可以在将请求委派给目标前后对其进行处理, 所以可能会改变最终结果。
  • 装饰器:当封装器实现了与其封装对象相同的接口就被称为装饰器。此时从客户端的角度来看, 这些对象是完全一样的。 封装器中的引用成员变量可以是遵循相同接口的任意对象。
  • 结构图:
  • 实现方法:
    • 1、确保业务逻辑可用一个基本组件及多个额外可选层次表示。
    • 2、找出基本组件和可选层次的通用方法。 创建一个组件接口并在其中声明这些方法。
    • 3、创建一个具体组件类, 并定义其基础行为。
    • 4、创建装饰基类, 使用一个成员变量存储指向被封装对象的引用。 该成员变量必须被声明为组件接口类型, 从而能在运行时连接具体组件和装饰。 装饰基类必须将所有工作委派给被封装的对象
    • 5、确保所有类实现组件接口。
    • 6、将装饰基类扩展为具体装饰。 具体装饰必须在调用父类方法 (总是委派给被封装对象) 之前或之后执行自身的行为。
    • 7、客户端代码负责创建装饰并将其组合成客户端所需的形式。

外观模式

  • 意图:能为程序库、 框架或其他复杂类提供一个简单的接口。
    • 外观类为包含许多活动部件的复杂子系统提供一个简单的接口。 与直接调用子系统相比, 外观提供的功能可能比较有限, 但它却包含了客户端真正关心的功能。
  • 结构图:
  • 实现方式:
    • 1、考虑能否在现有子系统的基础上提供一个更简单的接口。 如果该接口能让客户端代码独立于众多子系统类, 那么你的方向就是正确的。
    • 2、在一个新的外观类中声明并实现该接口。 外观应将客户端代码的调用重定向到子系统中的相应对象处。 如果客户端代码没有对子系统进行初始化, 也没有对其后续生命周期进行管理, 那么外观必须完成此类工作。
    • 3、如果要充分发挥这一模式的优势, 你必须确保所有客户端代码仅通过外观来与子系统进行交互。 此后客户端代码将不会受到任何由子系统代码修改而造成的影响, 比如子系统升级后, 你只需修改外观中的代码即可。
    • 4、如果外观变得过于臃肿, 你可以考虑将其部分行为抽取为一个新的专用外观类。

享元模式--缓存、Cache、Flyweight

  • 意图:摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的内存容量中载入更多对象。
  • 内在状态与外在状态:
    • 对象的常量数据通常被称为内在状态, 其位于对象中, 其他对象只能读取但不能修改其数值。
    • 而对象的其他状态常常能被其他对象 “从外部” 改变, 因此被称为外在状态
  • 享元模式建议不在对象中存储外在状态, 而是将其传递给依赖于它的一个特殊方法。 程序只在对象中保存内在状态, 以方便在不同情景下重用。 这些对象的区别仅在于其内在状态 (与外在状态相比, 内在状态的变体要少很多), 因此你所需的对象数量会大大削减。
  • 享元工厂:为了能更方便地访问各种享元, 你可以创建一个工厂方法来管理已有享元对象的缓存池。 工厂方法从客户端处接收目标享元对象的内在状态作为参数, 如果它能在缓存池中找到所需享元, 则将其返回给客户端; 如果没有找到, 它就会新建一个享元, 并将其添加到缓存池中。
  • 结构图:
  • 实现方式:
    • 1、将需要改写为享元的类成员变量拆分为两个部分:
      • 内在状态: 包含不变的、 可在许多对象中重复使用的数据的成员变量。
      • 外在状态: 包含每个对象各自不同的情景数据的成员变量
    • 2、保留类中表示内在状态的成员变量, 并将其属性设置为不可修改。 这些变量仅可在构造函数中获得初始数值。
    • 3、找到所有使用外在状态成员变量的方法, 为在方法中所用的每个成员变量新建一个参数, 并使用该参数代替成员变量。
    • 4、可以有选择地创建工厂类来管理享元缓存池, 它负责在新建享元时检查已有的享元。 如果选择使用工厂, 客户端就只能通过工厂来请求享元, 它们需要将享元的内在状态作为参数传递给工厂。
    • 5、客户端必须存储和计算外在状态 (情景) 的数值, 因为只有这样才能调用享元对象的方法。 为了使用方便, 外在状态和引用享元的成员变量可以移动到单独的情景类中。

代理模式--Proxy

  • 意图:代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。
  • 结构图:
  • 应用场景:
    • 延迟初始化 (虚拟代理)。 如果你有一个偶尔使用的重量级服务对象, 一直保持该对象运行会消耗系统资源时, 可使用代理模式。从而无需在程序启动时就创建该对象,延迟到真正有需要的时候。
    • 访问控制 (保护代理)。 如果你只希望特定客户端使用服务对象, 这里的对象可以是操作系统中非常重要的部分, 而客户端则是各种已启动的程序 (包括恶意程序), 此时可使用代理模式。
    • 本地执行远程服务 (远程代理)。 适用于服务对象位于远程服务器上的情形。代理通过网络传递客户端请求, 负责处理所有与网络相关的复杂细节。
    • 记录日志请求 (日志记录代理)。 适用于当你需要保存对于服务对象的请求历史记录时。代理可以在向服务传递请求前进行记录。
    • 缓存请求结果 (缓存代理)。 适用于需要缓存客户请求结果并对缓存生命周期进行管理时, 特别是当返回结果的体积非常大时。
    • 智能引用。 可在没有客户端使用某个重量级对象时立即销毁该对象。

行为模式

  • 行为模式负责对象间的高效沟通和职责委派。