C++ 设计模式笔记——2. 创建型模式
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
// 产品接口
struct Product {
virtual void describe() const = 0;
virtual ~Product() = default;
};
// 产品A
struct ProductA : Product {
void describe() const override { std::cout << "This is Product A.\n"; }
};
// 产品B
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;
};
// 工厂A
struct FactoryA : Factory {
std::unique_ptr<Product> createProduct() const override {
return std::make_unique<ProductA>();
}
};
// 工厂B
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
// 产品A接口
struct AbstractProductA {
virtual void describe() const = 0;
virtual ~AbstractProductA() = default;
};
// 产品B接口
struct AbstractProductB {
virtual void describe() const = 0;
virtual ~AbstractProductB() = default;
};
// 具体产品A1
struct ProductA1 : AbstractProductA {
void describe() const override { std::cout << "This is Product A1.\n"; }
};
// 具体产品A2
struct ProductA2 : AbstractProductA {
void describe() const override { std::cout << "This is Product A2.\n"; }
};
// 具体产品B1
struct ProductB1 : AbstractProductB {
void describe() const override { std::cout << "This is Product B1.\n"; }
};
// 具体产品B2
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;
};
// 具体工厂1
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>();
}
};
// 具体工厂2
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
// 产品类
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
// 抽象原型类
class AbstractPrototype {
public:
virtual ~AbstractPrototype() = default;
virtual std::unique_ptr<AbstractPrototype> clone() const = 0;
virtual void print() const = 0;
};
// 具体原型类A
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 << std::endl;
}
private:
int m_field;
};
// 具体原型类B
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 << std::endl;
}
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
15class Singleton {
protected:
Singleton() { std::cout << "Singleton: call Constructor\n"; };
static Singleton demo; // declare
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; // define
这里在类外的定义是必要的,否则在使用单例时会触发编译错误。
基于类的静态函数的局部静态变量(懒汉版) 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class 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
21class 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;
}
};
对于单例模式的使用是有争议的:
- 单例模式就像全局变量一样,到处都可以对其访问和修改,这不利于代码解耦,给代码测试和调试带来困难;
- 单例模式需要关注线程安全,增加了代码复杂度;
- 对于一个需要保证全局唯一性的资源管理类,才有必要使用单例模式。