创建型模式

设计模式学习笔记——创建型模式篇。 📑 相关文章:结构型模式 · 行为型模式

[toc]


一、工厂模式

PlantUML diagram

1.1 简单工厂模式

简单工厂模式由一个工厂类根据传入参数决定创建哪种产品实例,将对象的创建逻辑集中在一处。

类图

PlantUML diagram

代码

C++cpp
#include <iostream>
#include <string>
using namespace std;
 
// 抽象水果类
class Fruit {
public:
    virtual void getName() = 0;
    virtual ~Fruit() {}
};
 
class Apple : public Fruit {
public:
    void getName() override {
        cout << "我是苹果" << endl;
    }
};
 
class Banana : public Fruit {
public:
    void getName() override {
        cout << "我是香蕉" << endl;
    }
};
 
// 简单工厂类
class FruitFactory {
public:
    Fruit* createFruit(const string& type) {
        if (type == "apple") return new Apple;
        if (type == "banana") return new Banana;
        return nullptr;
    }
};
 
int main()
{
    FruitFactory* factory = new FruitFactory;
 
    Fruit* apple = factory->createFruit("apple");
    apple->getName();
 
    Fruit* banana = factory->createFruit("banana");
    banana->getName();
 
    delete apple;
    delete banana;
    delete factory;
 
    return 0;
}

优缺点

优点:

  1. 不需要记住具体类名,甚至连具体参数都不用记忆
  2. 实现了对象创建和使用的分离
  3. 系统可扩展性好,无需修改接口和原类

缺点:

  1. 增加系统中类的个数,复杂度和理解度增加
  2. 增加了系统的抽象性和理解难度
  3. 工厂类职责过重,新增产品需修改工厂代码(违反开闭原则)

1.2 工厂方法模式

工厂方法模式为每种产品提供对应的工厂子类,将简单工厂中的工厂类职责拆分,符合开闭原则。

类图

PlantUML diagram

代码

C++cpp
#include <iostream>
using namespace std;
 
// 抽象水果类
class Fruit {
public:
    virtual void getName() = 0;
    virtual ~Fruit() {}
};
 
class Apple : public Fruit {
public:
    void getName() override {
        cout << "我是苹果" << endl;
    }
};
 
class Banana : public Fruit {
public:
    void getName() override {
        cout << "我是香蕉" << endl;
    }
};
 
// 抽象工厂类
class AbstractFactory {
public:
    virtual Fruit* createFruit() = 0;
    virtual ~AbstractFactory() {}
};
 
// 苹果工厂:只生产苹果
class AppleFactory : public AbstractFactory {
public:
    Fruit* createFruit() override {
        return new Apple;
    }
};
 
// 香蕉工厂:只生产香蕉
class BananaFactory : public AbstractFactory {
public:
    Fruit* createFruit() override {
        return new Banana;
    }
};
 
int main()
{
    // 香蕉工厂 → 生产香蕉
    AbstractFactory* bananaFactory = new BananaFactory;
    Fruit* banana = bananaFactory->createFruit();
    banana->getName();
 
    delete bananaFactory;
    delete banana;
 
    // 苹果工厂 → 生产苹果
    AbstractFactory* appleFactory = new AppleFactory;
    Fruit* apple = appleFactory->createFruit();
    apple->getName();
 
    delete appleFactory;
    delete apple;
 
    return 0;
}

💡 简单工厂 → 工厂方法:将工厂类的 switch/if 判断拆分为多个工厂子类,每新增产品只需新增对应工厂类,无需修改已有代码。


二、抽象工厂模式

工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题。但 由于工厂方法模式中的每个工厂只生产一种产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,我们可以考虑将一些相关的产品组成一个”产品族”,由同一个工厂来统一生产,这就是抽象工厂模式的基本思想。

2.1 图例

PlantUML diagram

2.2 代码

C++cpp
#include <iostream>
using namespace std;
 
// ===== 抽象产品类 =====
class AbstractApple {
public:
    virtual void getName() = 0;
    virtual ~AbstractApple() {}
};
class AbstractBanana {
public:
    virtual void getName() = 0;
    virtual ~AbstractBanana() {}
};
class AbstractPear {
public:
    virtual void getName() = 0;
    virtual ~AbstractPear() {}
};
 
// ===== 具体产品:美国 =====
class USAApple : public AbstractApple {
public:
    void getName() override { cout << "美国苹果" << endl; }
};
class USABanana : public AbstractBanana {
public:
    void getName() override { cout << "美国香蕉" << endl; }
};
class USAPear : public AbstractPear {
public:
    void getName() override { cout << "美国鸭梨" << endl; }
};
 
// ===== 具体产品:中国 =====
class ChinaApple : public AbstractApple {
public:
    void getName() override { cout << "中国苹果" << endl; }
};
class ChinaBanana : public AbstractBanana {
public:
    void getName() override { cout << "中国香蕉" << endl; }
};
class ChinaPear : public AbstractPear {
public:
    void getName() override { cout << "中国鸭梨" << endl; }
};
 
// ===== 具体产品:日本 =====
class JapanApple : public AbstractApple {
public:
    void getName() override { cout << "日本苹果" << endl; }
};
class JapanBanana : public AbstractBanana {
public:
    void getName() override { cout << "日本香蕉" << endl; }
};
class JapanPear : public AbstractPear {
public:
    void getName() override { cout << "日本鸭梨" << endl; }
};
 
// ===== 抽象工厂类 =====
class AbstractFactory {
public:
    virtual AbstractApple* createApple() = 0;
    virtual AbstractBanana* createBanana() = 0;
    virtual AbstractPear* createPear() = 0;
    virtual ~AbstractFactory() {}
};
 
// ===== 具体工厂 =====
class USAFactory : public AbstractFactory {
public:
    AbstractApple* createApple() override { return new USAApple; }
    AbstractBanana* createBanana() override { return new USABanana; }
    AbstractPear* createPear() override { return new USAPear; }
};
 
class ChinaFactory : public AbstractFactory {
public:
    AbstractApple* createApple() override { return new ChinaApple; }
    AbstractBanana* createBanana() override { return new ChinaBanana; }
    AbstractPear* createPear() override { return new ChinaPear; }
};
 
class JapanFactory : public AbstractFactory {
public:
    AbstractApple* createApple() override { return new JapanApple; }
    AbstractBanana* createBanana() override { return new JapanBanana; }
    AbstractPear* createPear() override { return new JapanPear; }
};
 
int main()
{
    // 中国工厂 → 中国苹果、香蕉、鸭梨
    AbstractFactory* factory = new ChinaFactory;
    AbstractApple* AA = factory->createApple();
    AbstractBanana* AB = factory->createBanana();
    AbstractPear* AP = factory->createPear();
 
    AA->getName();
    AB->getName();
    AP->getName();
 
    delete AA;
    delete AB;
    delete AP;
    delete factory;
 
    return 0;
}

⚠️ 抽象工厂模式不符合”开闭原则”:新增产品等级结构需修改所有工厂类。

2.3 抽象工厂模式的优缺点

优点:

  1. 拥有工厂方法模式的优点
  2. 当一个产品族中的多个对象被设计成一起工作时,能够保证客户端始终只使用同一个产品族中的对象
  3. 增加新的产品族很方便,无需修改已有系统,符合”开闭原则”

缺点:

  1. 增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,违背了”开闭原则”

2.4 三种工厂模式对比

PlantUML diagram

简单工厂模式 + “开闭原则” = 工厂方法模式 工厂方法模式 + “产品族” = 抽象工厂模式

借鉴了《Easy搞定设计模式》


三、单例模式

保证一个类只有一个实例存在,同时提供能对该实例加以访问的全局访问方法。

3.1 三个要点

  1. 某个类只能有一个实例
  2. 它必须自行创建这个实例
  3. 它必须自行向整个系统提供这个实例

3.2 角色和职责

Singleton(单例):在单例类的内部实现只生成一个实例,同时提供一个静态的 getInstance() 方法,让客户可以访问其唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个 Singleton 类型的静态对象,作为外部共享的唯一实例。

3.3 单例模式的使用步骤

  1. 构造函数私有化 — 防止外部直接创建实例
  2. 提供全局静态方法 — 作为全局访问点获取单例对象
  3. 定义静态指针 — 指向本类的唯一实例

单例模式类图

PlantUML diagram

3.4 饿汉模式

在程序启动时(类加载时)就创建实例,不管是否使用。

C++cpp
#include <iostream>
using namespace std;
 
class Singleton {
public:
    static Singleton* getInstance() {
        return instance;
    }
private:
    Singleton() {}
    static Singleton* instance;
};
 
// 静态变量在类外初始化(程序启动即创建)
Singleton* Singleton::instance = new Singleton;
 
int main()
{
    Singleton* s1 = Singleton::getInstance();
    Singleton* s2 = Singleton::getInstance();
 
    if (s1 == s2)
        cout << "s1 == s2" << endl;
    else
        cout << "s1 != s2" << endl;
 
    return 0;
}

饿汉模式线程安全(初始化在 main 之前完成),但可能浪费资源(未使用也会创建)。

3.5 懒汉模式

在第一次使用时才创建实例,延迟加载。

C++cpp
class Singleton1 {
public:
    static Singleton1* getInstance() {
        if (instance1 == nullptr) {
            instance1 = new Singleton1;
        }
        return instance1;
    }
private:
    Singleton1() {}
    static Singleton1* instance1;
};
 
// 懒汉式初始化:初始为空,首次调用时创建
Singleton1* Singleton1::instance1 = nullptr;

⚠️ 懒汉模式在多线程下需加锁保护,否则可能创建多个实例。

饿汉 vs 懒汉对比

PlantUML diagram

3.6 单例模式的应用——打印机

用单例模式实现一个打印机程序,记录打印次数和内容。

C++cpp
#include <iostream>
#include <string>
using namespace std;
 
class Printer {
public:
    static Printer* getInstance() {
        return instance;
    }
 
    void print(const string& text) {
        cout << "打印内容:" << text << endl;
        sequence++;
        cout << "打印机今天使用了 " << sequence << " 次" << endl;
    }
 
private:
    // 嵌套 Garbo 类,用于程序结束时自动释放单例
    class Garbo {
    public:
        ~Garbo() {
            if (instance != nullptr) {
                delete instance;
                instance = nullptr;
            }
        }
    };
 
    Printer() {}
    static Printer* instance;
    static int sequence;
    static Garbo garbo;
};
 
int Printer::sequence = 0;
Printer* Printer::instance = new Printer;
Printer::Garbo Printer::garbo;
 
int main()
{
    Printer* p1 = Printer::getInstance();
    p1->print("一封简历");
 
    Printer* p2 = Printer::getInstance();
    p2->print("一份工作汇报");
 
    Printer* p3 = Printer::getInstance();
    p3->print("离职申请");
 
    // p1 == p2 == p3,始终是同一个实例
    return 0;
}

打印机类图

PlantUML diagram

四、总结

PlantUML diagram

以上就是几种常见的创建型设计模式。