创建型模式

工厂方法

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

    工厂方法

  • 注意:返回的产品需要共同的基类

  • 组成:产品、具体产品、创建者、具体创建者

  • 适用场景

    • 当你在编写代码时,如果无法预支对象确切类别和具体关系时,可采用工厂方法
    • 若果希望用户能扩展你的软件库或者框架的内部组件,可使用工厂方法
    • 如果你希望复用现有对象而节省资源,而非每次都重新创建对象,可使用工厂方法
  • 代码实例

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
class product {};
// 此处采用公有继承,否则无法实现多态的转换
class apple : public product {
void Print() {
std::cout << "I am apple" << std::endl;
}
};
class banana : public product {
void Print() {
std::cout << "I am banana" << std::endl;
}
};

class factory {
public:
virtual product* CreateProduct() = 0;
};

class appFac : public factory {
public:
virtual product* CreateProduct() {
return new apple;
}
};

class banFac : public factory {
public:
virtual product* CreateProduct() {
return new banana;
}
};

// 切记,在采用此方案时,产品模版和产品遵循相同的接口
  • 实现方式
    • 让所有产品遵循同一接口,该接口必须声明对所有产品都有意义的方法
      • 即提供一个模版父类,其具体实现交由其子类
    • 创建类中添加一个空的工厂方法,返回类型必须遵循通用的产品接口
    • 在创建者代码中,将构造产品的部分,均完全替换为对工厂方法的调用
  • 优缺点
    • 避免创建者和具体产品之间的精密耦合
    • 单一职责原则,可以产品的创建代码放在程序的一个位置,从而使得其更容易维护
    • 开闭原则,无需更改现有代码,而引入新的功能
    • 缺点:引入工厂方法,会需要引入许多新的子类

抽象工厂

  • 定义:抽象工厂是一种创建型设计模式,他能创建一系列的相关对象,而无需指定其具体类

抽象工厂

  • 组成:抽象产品、具体产品、抽象工厂、具体工厂

  • 个人理解:较之工厂模式,新增了对多种产品和工厂的支持,以适用于更为复杂的场景

  • 适用场景

    • 如果代码需要与多个不同系列的产品交互,出于对未来扩展性的考虑,可采用抽象工厂
    • 如果你有基于一组抽象方法的类,且主要功能不明确
  • 实现方式

    • 以不同的产品类型与产品变体为维度绘制矩阵
    • 为所有产品声明抽象产品接口,然后让所有具体产品类来实现此接口
    • 声明抽象工厂接口,并且在接口中为所有抽象产品提供一组构建方法
    • 为每个产品变体实现一个具体工厂类
    • 找出代码中所有对产品构造函数的调用,替换为工厂对象中相应的构建方法
  • 优缺点

    • 保障同一工厂生成的产品相互匹配
    • 你可以避免客户端和具体产品代码的耦合
    • 单一职责原则、开闭原则
    • 缺点:由于引入了众多的类和接口,代码可能更为复杂

生成器模式

  • 定义:生成器是一种创建型设计模式,使你能够分步骤创建复杂对象。该模式允许你使用相同的创建代码生成不同类型和形式的对象

    生成器

  • 生成器模式建议将对象构造代码从产品类中抽泣出来,并将其放在名为生成器的独立对象中

实例

  • 其中,可通过主管类来定义创建步骤的执行顺序,而生成器则提供这些步骤的实现
  • 组成:生成器、具体生成器、产品、主管、客户端
  • 实现代码
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/**
* It makes sense to use the Builder pattern only when your products are quite
* complex and require extensive configuration.
*
* Unlike in other creational patterns, different concrete builders can produce
* unrelated products. In other words, results of various builders may not
* always follow the same interface.
*/

class Product1{
public:
std::vector<std::string> parts_;
void ListParts()const{
std::cout << "Product parts: ";
for (size_t i=0;i<parts_.size();i++){
if(parts_[i]== parts_.back()){
std::cout << parts_[i];
}else{
std::cout << parts_[i] << ", ";
}
}
std::cout << "\n\n";
}
};


/**
* The Builder interface specifies methods for creating the different parts of
* the Product objects.
*/
class Builder{
public:
virtual ~Builder(){}
virtual void ProducePartA() const =0;
virtual void ProducePartB() const =0;
virtual void ProducePartC() const =0;
};
/**
* The Concrete Builder classes follow the Builder interface and provide
* specific implementations of the building steps. Your program may have several
* variations of Builders, implemented differently.
*/
class ConcreteBuilder1 : public Builder{
private:

Product1* product;

/**
* A fresh builder instance should contain a blank product object, which is
* used in further assembly.
*/
public:

ConcreteBuilder1(){
this->Reset();
}

~ConcreteBuilder1(){
delete product;
}

void Reset(){
this->product= new Product1();
}
/**
* All production steps work with the same product instance.
*/

void ProducePartA()const override{
this->product->parts_.push_back("PartA1");
}

void ProducePartB()const override{
this->product->parts_.push_back("PartB1");
}

void ProducePartC()const override{
this->product->parts_.push_back("PartC1");
}

/**
* Concrete Builders are supposed to provide their own methods for
* retrieving results. That's because various types of builders may create
* entirely different products that don't follow the same interface.
* Therefore, such methods cannot be declared in the base Builder interface
* (at least in a statically typed programming language). Note that PHP is a
* dynamically typed language and this method CAN be in the base interface.
* However, we won't declare it there for the sake of clarity.
*
* Usually, after returning the end result to the client, a builder instance
* is expected to be ready to start producing another product. That's why
* it's a usual practice to call the reset method at the end of the
* `getProduct` method body. However, this behavior is not mandatory, and
* you can make your builders wait for an explicit reset call from the
* client code before disposing of the previous result.
*/

/**
* Please be careful here with the memory ownership. Once you call
* GetProduct the user of this function is responsable to release this
* memory. Here could be a better option to use smart pointers to avoid
* memory leaks
*/

Product1* GetProduct() {
Product1* result= this->product;
this->Reset();
return result;
}
};

/**
* The Director is only responsible for executing the building steps in a
* particular sequence. It is helpful when producing products according to a
* specific order or configuration. Strictly speaking, the Director class is
* optional, since the client can control builders directly.
*/
class Director{
/**
* @var Builder
*/
private:
Builder* builder;
/**
* The Director works with any builder instance that the client code passes
* to it. This way, the client code may alter the final type of the newly
* assembled product.
*/

public:

void set_builder(Builder* builder){
this->builder=builder;
}

/**
* The Director can construct several product variations using the same
* building steps.
*/

void BuildMinimalViableProduct(){
this->builder->ProducePartA();
}

void BuildFullFeaturedProduct(){
this->builder->ProducePartA();
this->builder->ProducePartB();
this->builder->ProducePartC();
}
};
/**
* The client code creates a builder object, passes it to the director and then
* initiates the construction process. The end result is retrieved from the
* builder object.
*/
/**
* I used raw pointers for simplicity however you may prefer to use smart
* pointers here
*/
void ClientCode(Director& director)
{
ConcreteBuilder1* builder = new ConcreteBuilder1();
director.set_builder(builder);
std::cout << "Standard basic product:\n";
director.BuildMinimalViableProduct();

Product1* p= builder->GetProduct();
p->ListParts();
delete p;

std::cout << "Standard full featured product:\n";
director.BuildFullFeaturedProduct();

p= builder->GetProduct();
p->ListParts();
delete p;

// Remember, the Builder pattern can be used without a Director class.
std::cout << "Custom product:\n";
builder->ProducePartA();
builder->ProducePartC();
p=builder->GetProduct();
p->ListParts();
delete p;

delete builder;
}

int main(){
Director* director= new Director();
ClientCode(*director);
delete director;
return 0;
}
  • 适合场景
    • 使用生成器模式,可以避免重叠构造函数
      • 在C++中采用委托构造函数也可实现相同的需求
    • 当你希望使用代码创建不同形式的产品,可采用生成器模式
    • 使用生成器构造组合树或其他复杂对象
  • 实现方法
    • 清晰的定义通用步骤,确保可以制造所有形式的产品
    • 在基本生成器接口声明这些步骤
    • 为每个形式的产品创建具体生成器类
    • 考虑主管类(用于指导生成器调用顺序)
    • 客户端代码会同时创建生成器和主管对象
  • 优缺点
    • 可分布创建对象
    • 生成不同形式的产品是,可复用相同的制造代码
    • 单一职责原则
    • 缺点:该模式需要新增多个类,因此代码的整体复杂度可能有所增加

原型

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

    • 由于从外部复制对象总非是可行的,毕竟有私有对象的存在

    原型

  • 组成:原型、具体原型、客户端(原型注册表hash)

  • 适用场景

    • 如果你需要复制一些对象,同时又希望代码独立于这些对象所属的具体类,可使用原型模式
    • 如果子类的区别仅在于对象的初始化方式,那么可以采用此模式减少子类的数量
  • 实现方式

    • 创建原型接口,并且声明克隆方法
    • 原型类必须另行定义一个以该类对象为参数的构造函数
      • 拷贝构造函数
    • 克隆方法通常只有一行代码,使用new调用原型坂本的构造函数
    • 你可以创建一个中心化原型注册表,用于存储常用原型
  • 优缺点

    • 你可以克隆对象
    • 你可以克隆预生成原型,避免反复运行初始化
    • 你可以更方便生成复杂对象
    • 你可以用集成以外的方式来处理复杂对象的不同配置
    • 缺点:克隆包含循环引用的复杂对象可能会非常麻烦
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
#include <iostream>
#include <string>

using namespace std;

class CPrototype
{
public:

CPrototype() {}

~CPrototype() {}

virtual CPrototype* Clone() = 0;
};

class CWukong : CPrototype
{
public:
CWukong() : mAge(0), mHeightCM(100), mHair(10000), mLockRing(0), mFightCapacity (20)
{};

CWukong(CWukong *rhs)
{
mName = rhs->mName;
mAge = rhs->mAge;
mHeightCM = rhs->mHeightCM;
mHair = rhs->mHair;
mLockRing = rhs->mLockRing;
mFightCapacity = rhs->mFightCapacity;
};

virtual ~CWukong() {};

CWukong* Clone()
{
return new CWukong(*this);
};

private:
string mName;
int mAge;
int mHeightCM;
int mHair;
bool mLockRing;
int mFightCapacity;
};

单例

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

    • 保障一个类只有一个实例
      • 无法通过构造函数实现
      • 用于对共享资源的控制
    • 为该实例提供一个全局访问节点

    单例模式

  • 解决方案

    • 将默认构造函数设为私有
    • 新建一个静态构建方法作为构造函数
  • 组成:单例

  • 适合使用场景

    • 如果程序中某个类对所有客户端都只有一个可用的实例
    • 如果你需要更加严格地控制全局变量
  • 实现方式

    • 在类中添加一个私有静态成员变量用于保存单例实例
    • 声明一个公有静态构建方法获取单例实例
    • 在静态方法中实现延迟初始化,即首次调用创建新对象,并存储在成员变量中,此后该方法都返回先前存储
    • 将累的构造函数设为私有
    • 检查客户端代码,将对单例的构造函数调用替换为静态方法的调用
  • 优缺点

    • 你可以保证一个类只有一个实例
    • 你获得了一个指向该实例的全局访问节点
    • 仅在首次请求单例是对其进行初始化
    • 违反了单一职责原则
    • 单例模式可能掩盖不良设计
  • 代码实现

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
// 饿汉式单例模式 程序启动便初始化
class CSingleton {
public:
// 线程安全
static CSingleton* getInstance() {
return &single;
}

private:
static CSingleton single;
CSingleton();
~CSingleton();
};
CSingleton CSingleton::single;

// 懒汉式单例模式 调用后方初始化
class CSingleton {
public:
// 多线程的情形下,考虑加锁
static CSingleton* getInstance() {
if (single == nullptr) {
single = new CSingleton();
}
return single;
}

private:
static CSingleton* single;
CSingleton();
~CSingleton();

//注意如果没有下面的释放资源类,在main函数中将手动释放资源
class CRelease {
public:
~CRelease() { delete single; }
};
static CRelease release;

};