Factory Method (工厂方法)
工厂方法模式是一种创建型设计模式,其在父类中提供一个创建对象的方法,允许子类决定实例化对象的类型。
我们需要定义两个抽象类:产品基类Product和工厂基类Factory,后者提供创建产品的方法createProduct()。
具体产品(ProductA、ProductB)需要继承自产品基类Product。
每一个产品都需要提供配套的工厂(FactoryA、FactoryB),工厂需要继承自工厂基类Factory,对创建产品的方法提供不同的实现。
示例代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| #include <iostream> #include <memory>
struct Product { virtual void describe() const = 0; virtual ~Product() = default; };
struct ProductA : Product { void describe() const override { std::cout << "This is Product A.\n"; } };
struct ProductB : Product { void describe() const override { std::cout << "This is Product B.\n"; } };
struct Factory { virtual std::unique_ptr<Product> createProduct() const = 0; virtual ~Factory() = default; };
struct FactoryA : Factory { std::unique_ptr<Product> createProduct() const override { return std::make_unique<ProductA>(); } };
struct FactoryB : Factory { std::unique_ptr<Product> createProduct() const override { return std::make_unique<ProductB>(); } };
void clientCode(const Factory &factory) { auto product = factory.createProduct(); product->describe(); }
int main() { std::cout << "Client: Working with a Factory A:\n"; FactoryA productAFactory; clientCode(productAFactory);
std::cout << "\nClient: Working with a Factory B:\n"; FactoryB productBFactory; clientCode(productBFactory);
return 0; }
|
Abstract Factory (抽象工厂)
抽象工厂模式是一种创建型设计模式,它能创建一系列相关的对象,而无需指定其具体类。
我们需要定义多个抽象产品接口(AbstractProductA 和 AbstractProductB),
每个抽象产品接口有不同的实现(ProductA1 和 ProductA2,ProductB1 和 ProductB2)。
我们还需要定义一个抽象工厂接口(AbstractFactory),以及两个具体工厂类(ConcreteFactory1 和 ConcreteFactory2),
它们可以提供不同的产品创建方案,在多个产品的不同实现之间自由搭配:
ConcreteFactory1 提供 ProductA1 和 ProductB1 的创建
ConcreteFactory2 提供 ProductA2 和 ProductB2 的创建
示例代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| #include <iostream> #include <memory>
struct AbstractProductA { virtual void describe() const = 0; virtual ~AbstractProductA() = default; };
struct AbstractProductB { virtual void describe() const = 0; virtual ~AbstractProductB() = default; };
struct ProductA1 : AbstractProductA { void describe() const override { std::cout << "This is Product A1.\n"; } };
struct ProductA2 : AbstractProductA { void describe() const override { std::cout << "This is Product A2.\n"; } };
struct ProductB1 : AbstractProductB { void describe() const override { std::cout << "This is Product B1.\n"; } };
struct ProductB2 : AbstractProductB { void describe() const override { std::cout << "This is Product B2.\n"; } };
struct AbstractFactory { virtual std::unique_ptr<AbstractProductA> createProductA() const = 0; virtual std::unique_ptr<AbstractProductB> createProductB() const = 0; virtual ~AbstractFactory() = default; };
struct ConcreteFactory1 : AbstractFactory { std::unique_ptr<AbstractProductA> createProductA() const override { return std::make_unique<ProductA1>(); }
std::unique_ptr<AbstractProductB> createProductB() const override { return std::make_unique<ProductB1>(); } };
struct ConcreteFactory2 : AbstractFactory { std::unique_ptr<AbstractProductA> createProductA() const override { return std::make_unique<ProductA2>(); }
std::unique_ptr<AbstractProductB> createProductB() const override { return std::make_unique<ProductB2>(); } };
void clientCode(const AbstractFactory &factory) { auto productA = factory.createProductA(); auto productB = factory.createProductB(); productA->describe(); productB->describe(); }
int main() { std::cout << "Client: Testing client code with the first factory type:\n"; ConcreteFactory1 factory1; clientCode(factory1);
std::cout << "\nClient: Testing the same client code with the second " "factory type:\n"; ConcreteFactory2 factory2; clientCode(factory2);
return 0; }
|
Builder (建造者/生成器)
建造者模式是一种创建型设计模式,又称为生成器模式,它允许我们使用简单的创建代码生成具有复杂配置的对象。
产品类House的配置比较繁琐:需要调用不同的接口,可能需要传递很多参数,可能还与顺序相关。
我们使用建造者类HouseBuilder将配置过程打包,提供成套的方案:
- 基本房屋
buildBasicHouse()
- 完整房屋
buildFullHouse()
调用者可以通过HouseBuilder方便地完成对象的创建,同时House也保留了灵活的配置能力。
示例代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| #include <iostream> #include <memory> #include <string>
class House { public: void setFoundation(const std::string &foundation) { m_foundation = foundation; }
void setStructure(const std::string &structure) { m_structure = structure; }
void setRoof(const std::string &roof) { m_roof = roof; }
void setInterior(const std::string &interior) { m_interior = interior; }
void describe() const { std::cout << "House with the following parts:\n"; std::cout << " - Foundation: " << m_foundation << "\n"; std::cout << " - Structure: " << m_structure << "\n"; std::cout << " - Roof: " << m_roof << "\n"; std::cout << " - Interior: " << m_interior << "\n"; }
private: std::string m_foundation; std::string m_structure; std::string m_roof; std::string m_interior; };
class HouseBuilder { public: HouseBuilder() { m_house = std::make_unique<House>(); }
HouseBuilder &buildFullHouse() { m_house->setFoundation("Concrete"); m_house->setStructure("Wood"); m_house->setRoof("Shingles"); m_house->setInterior("Drywall"); return *this; }
HouseBuilder &buildBasicHouse() { m_house->setFoundation("Concrete"); m_house->setStructure("Wood"); return *this; }
std::unique_ptr<House> getHouse() { return std::move(m_house); }
private: std::unique_ptr<House> m_house; };
int main() { std::cout << "Building a full house:\n"; auto fullHouse = HouseBuilder{}.buildFullHouse().getHouse(); fullHouse->describe();
std::cout << "Building a basic house:\n"; auto basicHouse = HouseBuilder{}.buildBasicHouse().getHouse(); basicHouse->describe();
return 0; }
|
建造者模式的核心就是把具有复杂配置的对象的创建过程简化,上面代码提供的只是一种示例,还有不同的建造者实现:
- 除了提供成套的创建方案,
HouseBuilder也可以提供分步创建的接口,客户端可以使用链式调用完成分步创建,在创建过程中,HouseBuilder还可以提供reset()重置之前的配置。
- 可以直接将建造者
HouseBuilder声明为House的友元,从而完全掌握后者的创建:House自身不再需要提供各种配置接口,各种接口可以前移到HouseBuilder中。
- 如果
House没有提供各种配置接口,只提供了一个具有很多参数的构造方法,那么HouseBuilder可以在内部记录下对应的参数列表,最后一步调用构造方法完成创建。对于参数列表的记录可以直接实现,例如作为HouseBuilder的公开数据成员即可。
使用示例如下(对应的代码略)
1
| auto house = HouseBuilder{}.setFoundation("Concrete").setStructure("Wood").getHouse();
|
C++提供的右值引用和移动也可以用于这里的建造者模式:让建造者单步构造过程的返回值是右值,并且对返回值要求[[nodiscard]]。
Prototype (原型)
原型模式是一种创建型设计模式,允许我们复制已有对象,同时无需使代码依赖它们所属的类。(支持复制的对象称为原型,通常提供名为clone()的方法)
抽象原型类AbstractPrototype要求提供clone()接口,具体的原型类PrototypeA和PrototypeB继承并实现了clone()方法。
示例代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| #include <iostream> #include <memory>
class AbstractPrototype { public: virtual ~AbstractPrototype() = default; virtual std::unique_ptr<AbstractPrototype> clone() const = 0; virtual void print() const = 0; };
class PrototypeA : public AbstractPrototype { public: explicit PrototypeA(int field) : m_field(field) {}
PrototypeA(const PrototypeA &other) : m_field(other.m_field) {}
PrototypeA &operator=(const PrototypeA &) = default; PrototypeA &operator=(PrototypeA &&) = default; PrototypeA(PrototypeA &&) = default; ~PrototypeA() override = default;
std::unique_ptr<AbstractPrototype> clone() const override { return std::make_unique<PrototypeA>(*this); }
void print() const override { std::cout << "PrototypeA: " << m_field << '\n'; }
private: int m_field; };
class PrototypeB : public AbstractPrototype { public: explicit PrototypeB(std::string field) : m_field(std::move(field)) {}
PrototypeB(const PrototypeB &other) : m_field(other.m_field) {}
PrototypeB(PrototypeB &&) = default; PrototypeB &operator=(const PrototypeB &) = default; PrototypeB &operator=(PrototypeB &&) = default; ~PrototypeB() override = default;
std::unique_ptr<AbstractPrototype> clone() const override { return std::make_unique<PrototypeB>(*this); }
void print() const override { std::cout << "PrototypeB: " << m_field << '\n'; }
private: std::string m_field; };
int main() { auto prototypeA = std::make_unique<PrototypeA>(10); auto prototypeB = std::make_unique<PrototypeB>("example");
auto cloneA = prototypeA->clone(); auto cloneB = prototypeB->clone();
cloneA->print(); cloneB->print();
return 0; }
|
Singleton (单例)
单例模式是一种创建型设计模式,它能够保证一个类只有一个实例,并提供访问该实例的全局方法。按照构造时机可以分为饿汉版(程序启动时构造)和懒汉版(第一次调用时构造)。
单例模式可以说是最简单的设计模式,对于Java这种完全面向对象的语言才有必要性,对于C++来说,直接使用全局变量或静态变量就可以实现单例的基本效果。
但是即使在C++中,我们一般也不会采用全局变量的方式,因为存在相互依赖的全局变量构造和析构顺序不确定,而且我们有时希望延迟构造时机到第一次调用时。
C++中有很多种方式都可以实现单例模式,下面提供几种示例。
基于类的静态变量(饿汉版)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class Singleton { protected: Singleton() { std::cout << "Singleton: call Constructor\n"; };
static Singleton demo; public: Singleton(const Singleton &) = delete; Singleton &operator=(const Singleton &) = delete;
virtual ~Singleton() { std::cout << "Singleton: call Destructor\n"; }
static Singleton &get_instance() { return demo; } };
Singleton Singleton::demo;
|
这里在类外的定义是必要的,否则在使用单例时会触发编译错误。
基于类的静态函数的局部静态变量(懒汉版)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class Singleton { protected: Singleton() { std::cout << "Singleton: call Constructor\n"; };
public: Singleton(const Singleton &) = delete; Singleton &operator=(const Singleton &) = delete;
virtual ~Singleton() { std::cout << "Singleton: call Destructor\n"; }
static Singleton &get_instance() { static Singleton demo; return demo; } };
|
如果希望用new在堆上创建单例对象,就需要做很多保护措施确保线程安全和异常安全等,例如下面的饿汉版单例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Singleton { protected: Singleton() { std::cout << "Singleton: call Constructor\n"; };
inline static std::atomic<Singleton *> demo{nullptr}; inline static std::mutex lock;
public: Singleton(const Singleton &) = delete; Singleton &operator=(const Singleton &) = delete;
virtual ~Singleton() { std::cout << "Singleton: call Destructor\n"; }
static Singleton *get_instance() { if (demo == nullptr) { std::lock_guard lc{lock}; if (demo == nullptr) { demo = new Singleton(); } } return demo; } };
|
对于单例模式的使用是有争议的:
- 单例模式就像全局变量一样,到处都可以对其访问和修改,这不利于代码解耦,给代码测试和调试带来困难;
- 单例模式需要关注线程安全,增加了代码复杂度;
- 对于一个需要保证全局唯一性的资源管理类,才有必要使用单例模式。