创建型模式提供了创建对象的机制, 旨在提升已有代码的灵活性、可复用性、可扩展性

单例模式

Singleton

问题

  1. 只需要一个实例
  2. 能方便获取实例

    方案

  3. 构造函数私有化

  4. 提供静态函数来构造

    意图

    保证一个类只有一个实例, 并提供一个访问该实例的全局节点。

    结构特征

  • 唯一实例
  • 访问入口 创建型设计模式分享 - 图2

    优点

  • 减少内存分配

  • 减少资源占用,如写文件

    缺点

  • 违背单一职责原则,一个类应该关心其内部功能,不应该关心外部如何初始化自身

    应用场景

  • 饿汉模式,多见于游戏开发,加载程序后一次性初始化所有对象,后续不会再有内存分配

  • 懒汉模式,模块化场景,不同模块在不同客户环境必要性不同,减少内存分配,需要注意并发安全

    与其他设计模式的关系

  • 可以与其他创建型设计模式组合使用

    简单工厂

    非设计模式,又叫静态工厂方法,Static Factory Method

问题

需要经常创建不同风格的椅子,存在大量下列代码,有什么问题?

  1. auto chair1 = new ArtDecoChair()
  2. auto chair2 = new VictorianChair()
  3. auto chair3 = new ModernChair()
  1. 不同风格的椅子创建时存在相同工序,因此存在重复代码
  2. 对调用者不友好,需要关心不同椅子的创建方法

    方案

  3. 抽象产品

  4. 定义专门类来管理

    意图

    专门定义一个类来创建其他类的实例,其中被创建的实例是相似的

    结构特征

  5. 产品抽象类,申明接口

  6. 产品具体类,实现接口
  7. 专门的工厂类管理产品 创建型设计模式分享 - 图3

    优点

  • 调用者不必关心对象创建和组织细节,从直接创建具体产品对象的尴尬局面摆脱出来,明确了职责

    缺点

  • 工厂类集中了所有实例的创建逻辑,违反了高内聚的责任分配原则,扩展时需要修改工厂类

  • 具体产品增多后,工厂类创建逻辑会愈发复杂难以维护

    应用场景

    只在很简单的情况下应用,比如:

  • 日志记录器:写入到文件、标准输出、数据库

    工厂方法模式

    Factory Method,Virtual Constructor

问题

简单工厂的职责过于复杂,比如要造好多种椅子

方案

  1. 抽象产品
  2. 工厂返回创建产品的工厂方法,该方法返回的对象与抽象产品接口相匹配

    意图

    是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。

    结构特征

  3. 产品抽象类,申明接口

  4. 产品具体类,实现接口
  5. 工厂类申明返回产品对象的工厂方法,返回的对象类型与产品抽象匹配
  6. 工厂具体类,实现工厂方法

structure (1).png

  1. 可以将工厂方法申明为抽象方法,也可以在基础工厂方法返回默认产品类型
  2. 工厂方法不一定每次会新建对象,可以返回缓存、对象池或其他来源的已有对象

优点

  • 避免创建者与产品耦合
  • 单一职责,创建逻辑独立
  • 开闭原则,容易引入新产品

    缺点

  • 代码层次变多变复杂

    应用场景

  • 编写代码的过程中, 如果无法预知对象确切类别及其依赖关系时

  • 如果你希望用户能扩展你软件库或框架的内部组件
  • 如果你希望复用现有对象来节省系统资源, 而不是每次都重新创建对象

    与其他设计模式的关系

  • 项目初期大都会使用工厂模式,后期可能会演化为抽象工厂方法模式,原型模式和生成器模式

    抽象工厂模式

    Abstract Factory

问题

  1. 需要创建一系列相关产品
  2. 多种系列

    方案

  3. 抽象产品

  4. 抽象工厂

    意图

    创建一系列相关的对象, 而无需指定其具体类。

    结构特征

  5. 抽象产品

  6. 具体产品
  7. 抽象工厂
  8. 具体工厂
  9. 具体工厂返回的对象实例与抽象产品匹配

structure.png

优点

  • 方便创建系列产品

    缺点

  • 代码愈加复杂

    应用场景

  • 存在系列产品,如 QQ 秀换皮肤,一套一套换

    生成器模式

    建造者模式,Builder

问题

对象构造时参数过多,并非所有参数需要,比如下列示例如何优化?

class House {
public:
    House(
        int windows,
        int doors,
        int rooms,
        bool hasGarden,
        bool hasSwimPool,
        bool hasGarage
    ){
        cout << "A new house!" << endl;
    }
};

方案

  1. 定义通用步骤,基本生成器中申明这些步骤
  2. 实现具体生成器
  3. 可以考虑增加主管类来创建一些使用固有步骤创建的产品

    意图

    分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象。

    结构特征

  4. 生成器抽象

  5. 具体生成器
  6. 最终产品
  7. 主管类(可选)
  8. 主管类与某个生成器关联

创建型设计模式分享 - 图6

优点

  • 可以分步创建对象, 暂缓创建步骤或递归运行创建步骤
  • 生成不同形式的产品时, 你可以复用相同的制造代码
  • 单一职责原则。 你可以将复杂构造代码从产品的业务逻辑中分离出来

    缺点

  • 代码复杂度增加

    应用场景

  • 避免大量构造函数

    class Pizza {
      Pizza(int size) { ... }
      Pizza(int size, boolean cheese) { ... }
      Pizza(int size, boolean cheese, boolean pepperoni) { ... }
      // ...
    
  • 需要创建不同形式的产品

  • 对象创建复杂,但可以分解成多个独立步骤

    可以优化,链式调用

原型模式

Clone,ProtoType

问题

如果你有一个对象, 并希望生成与其完全相同的一个复制品, 你该如何实现呢?
有些对象可能拥有私有成员变量, 它们在对象本身以外是不可见的,这是又该如何实现?

方案

将克隆过程委派给被克隆的实际对象

意图

能够复制已有对象, 而又无需使代码依赖它们所属的类。

结构特征

  1. 克隆接口
  2. 具体原型
  3. 客户端使用原型对象做克隆

    structure (2).png

    优点

  • 可以克隆对象, 而无需与它们所属的具体类相耦合
  • 避免反复运行初始化代码
  • 可以更方便地生成复杂对象

    缺点

  • 代码复杂度增加

    应用场景

  • 如果你需要复制一些对象, 同时又希望代码独立于这些对象所属的具体类(这一点考量通常出现在代码需要处理第三方代码通过接口传递过来的对象时)

  • 可以使用一系列预生成的、 各种类型的对象作为原型直接克隆,有时候克隆成本远小于构造

    示例

    builder.cpp
    factory_v1.cpp
    factory_v2.cpp
    factory_v3.cpp
    main.cpp