跳转至

7.3.创建型模式

工厂方法

场景:创建者基类负责实现业务逻辑,以及声明创建产品类的接口(即工厂方法1)。让创建者子类重写工厂方法,决定实例化哪个产品类。不同的产品类在业务逻辑中被统一调用,业务逻辑无需关心被调用的产品类具体是哪个产品,因此所有产品类必须提供相同的接口。

意图:将业务逻辑代码和需要不断扩展的产品类代码隔离。

structure-indexed.png
  • 产品(Product):对其子类应有的接口进行声明,这些接口将被创建者的业务逻辑(someOperation方法)调用。

  • 具体产品(Concrete Products):是产品接口的不同实现。这是具体创建者的工厂方法将返回的对象。

  • 创建者类(Creator):声明返回产品对象的工厂方法(createProduct)。Cre­ator类的工厂方法可以声明为抽象方法,强制子类重写该方法,也可以返回一个默认的产品对象。

  • 具体创建者(Concrete Creator):重写Creator类的工厂方法,决定返回具体的产品对象。包含一些与产品相关的核心业务逻辑,在这些业务代码中,工厂方法将被调用,获取具体产品对象,然后具体产品对象的相关接口将被调用,以实现具体的业务逻辑。

  • 客户端:客户端只需要调用Concrete Creator的接口。

因此,虽然创建者的名字是创建者,但实际上,创建者的主要任务是实现业务逻辑,而不是决定创建哪个产品的对象。 工厂方法将这些业务逻辑从具体产品类中分离出来。

并不一定每次调用工厂方法都会创建新的实例 。工厂方法也可以返回缓存、对象池或其他来源的已有对象。

可以只使用单一的工厂方法(一个具体创建者子类),但如果产品类太多的话,会导致工厂方法出现太多的分支语句,用于选择各种需要实例化的产品类。

适用场景

当你在编写代码的过程中,如果无法预知对象确切类别及其依赖关系时,可使用工厂方法。

工厂方法将创建产品的代码与实际使用产品的代码分离,从而能在不影响其他代码的情况下扩展产品创建部分代码。例如,如果需要向应用中添加一种新产品,你只需要开发新的创建者子类,然后重写其工厂方法即可。

抽象工厂

抽象工厂实际上是一组工厂方法。

如下图,现在有三种家具:椅子、沙发、咖啡桌,同时家具可以有三种风格:现代、维多利亚、装饰风艺术。

abstract-factory_problem-zh.png
系列产品及其不同变体

那么,我们可以为三种家具创建基类,其子类分别是三种风格的家具。

abstract-factory_solution1.png
同一对象的所有变体都必须放置在同一个类层次结构之中

同时写三个具体创建者类,分别对应三种风格,每个具体创建者都有三个工厂方法,分别用于创建具体风格的家具。

abstract-factory_solution2.png
每个具体工厂类都对应一个特定的风格

一般情况下,应用程序会在初始化阶段创建具体工厂对象 。而在此之前,应用程序必须根据配置文件或环境设定选择工厂类别。

abstract-factory_structure-indexed.png
  • 抽象产品(Abstract Prod­uct ):为构成系列产品的一组不同但相关的产品声明接口 。
  • 具体产品(Con­crete Prod­uct):是抽象产品的多种不同类型实现。所有变体(维多利亚/现代)都必须实现相应的抽象产品( 椅子/沙发 )。
  • 抽象工厂(Abstract Fac­to­ry):接口声明了一组创建各种抽象产品的方法。
  • 具体工厂(Con­crete Fac­to­ry):实现抽象工厂的构建方法。每个具体工厂都对应特定产品变体。
  • 客户端:业务逻辑调用Con­crete Fac­to­ry的工厂方法,得到具体的产品。

适用场景

如果代码需要与多个不同系列的相关产品交互,但是由于无法提前获取相关信息,或者出于对未来扩展性的考虑,你不希望代码基于产品的具体类进行构建,在这种情况下,可以使用抽象工厂。

生成器 - Builder

假设有这样一个复杂的产品类对象,在对其进行构造时需要对诸多成员变量和嵌套对象进行繁复的初始化工作。这些初始化代码通常深藏于一个包含众多参数且让人基本看不懂的构造函数中;甚至还有更糟糕的情况,那就是这些代码散落在客户端代码的多个位置。

image-20200810105359263

  • 生成器(Builder):接口声明在所有类型生成器中通用的产品构造步骤。

  • 具体生成器(Concrete Builders):提供构造过程的不同实现 。具体生成器也可以构造不遵循通用接口的产品。

  • 产品(Products):最终生成的对象。 由不同生成器构造的产品无需属于同一类层次结构或接口

  • 主管(Director ) 类:定义调用构造步骤的顺序 , 这样你就可以创建和复用特定的产品配置 。

  • 客户端 ( Client ) 必须将某个生成器对象与主管类关联 。 一般情况下 , 你只需通过主管类构造函数的参数进行一次性关联即可 。 此后主管类就能使用生成器对象完成后续所有的构造任务 。 但在客户端将生成器对象传递给主管类制造方法时还有另一种方式 。 在这种情况下 , 你在使用主管类生产产品时每次都可以使用不同的生成器

实际上,具体生成器还需要依赖一个原始产品类,该原始产品类经过主管类所定义构造步骤,被加工成最终产品对象。

专门用于生产一系列相关对象。抽象工厂 会马上返回产品,生成器 则允许你在获取产品前执行一些额外构造步骤。你可以在创建复杂 组合 树时使用 生成器,因为这可使其构造步骤以递归的方式运行。你可以结合使用 生成器 和 桥接 模式:主管 类负责抽象工作,各种不同的生成器

原型

原型 是一种创建型设计模式,使 你能够复制已有对象,而又无需 使代码依赖它们所属的类。

复制一个对象有两种方法:

  1. 从外部复制,创建一个与原始对象属于相同类的对象,遍历原始对象的所有成员变量,并将成员变量的值复制到新对象中。

这会有两个问题:

这要求你必须知道原始对象所属的类,所以代码必须依赖该类。即使你可以接受额外的依赖性,但有时对象的类可能是不知道的。

有些对象可能拥有私有成员变量,无法从外部访问。

  1. 从内部复制:复制过程由原始对象实现,调用原始对象的相关接口,便可返回一个克隆对象。支持克隆的对象便称为原型。

当你的对象有几十个成员变量和几百种类型时对其进行克隆甚至可以代替子类的构造。

image-20200810111703212

1.原型( Prototype)接口将对克隆方法进行声明。在绝大多数情況下,其中只会有一个名为 clone克隆的方法。

2.具体原型( Concrete Prototype)类将实现克隆方法。除了将原始对象的数据复制到克隆体中之外,该方法有时还需处理克隆过程中的极端情况,例如克隆关联对象和梳理递归依赖等等。

3.客户端( Client)可以复制实现了原型接口的任何对象

单例

单例是一种创建型设计模式, 让你能够保证一个类只有一个实例,并提供一个访问该 实例的全局节点。


  1. 工厂方法有两个含义:1. 具体指代创建者类的创建产品类的函数;2.指代工厂方法模式。需要根据上下文确定是哪个含义。