C++设计模式一一创建型模式

创建型模式

我们这里介绍四种对象创建型模式

  • ABSTRACT FACTORY 抽象工厂

  • FACTORY 工厂模式

  • PROTOTYPE 原型模式

  • SINGLETON 单例模式

抽象工厂模式

抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

UML图

例子:

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
/* 数据库访问程序设计,
* 不同的数据库访问方式可能不一样,
* 为了抽象对对不同数据库的访问,
* 可以将数据库隐藏起来,提供统一的访问方式,
* 用多态进行实现
*/
#include <iostream>
using namespace std;
// user表接口
class IUser
{
public:
virtual void Insert() = 0;
virtual void GetUser() = 0;
};
// SqlServer
class SqlServerUser : public IUser
{
public:
void Insert()
{
cout << "在SqlServer中给user表增加一条记录" << endl;
}
void GetUser()
{
cout << "在SqlServer中给user表获取一条记录" << endl;
}
};
// Access
class AccessUser : public IUser
{
public:
void Insert()
{
cout << "在Access中给user表增加一条记录" << endl;
}
void GetUser()
{
cout << "在Access中给user表获取一条记录" << endl;
}
};
// Department表 接口
class IDepartment
{
public:
virtual void Insert() = 0;
virtual void GetDepartment() = 0;
};
// SqlServer
class SqlServerDepartment : public IDepartment
{
public:
void Insert()
{
cout<<"在SQL Server中给Department表增加一条记录"<<endl;
}
void GetDepartment()
{
cout<<"在SQL Server中Department获取一条记录"<<endl;
}
};
// Access
class AccessDepartment : public IDepartment
{
public:
void Insert()
{
cout<<"在Access中给Department表增加一条记录"<<endl;
}
void GetDepartment()
{
cout<<"在Access中Department获取一条记录"<<endl;
}
};
// 抽象工厂接口
class IFactory
{
public:
virtual IUser* CreateUser() = 0;
virtual IDepartment* CreateDepartment() = 0;
};
// SqlServer工厂实现
class SqlServerFactory : public IFactory
{
IUser* CreateUser()
{
return new SqlServerUser();
}
IDepartment* CreateDepartment()
{
return new SqlServerDepartment();
}
};
// Access工厂实现
class AccessFactory : public IFactory
{
IUser* CreateUser()
{
return new AccessUser();
}
IDepartment* CreateDepartment()
{
return new AccessDepartment();
}
};
// 客户代码
int main()
{
//创建工厂
IFactory *pFactory = NULL;
IUser *pUser = NULL;
IDepartment *pDepartment = NULL;
int choice;
cout << "选择数据库:";
cin >> choice;
switch(choice)
{
case 1:
pFactory = new SqlServerFactory(); // 创建 SqlServer 访问的工厂
break;
case 2:
pFactory = new AccessFactory(); // 创建Access访问的工厂
break;
}
// 一致的操作
pUser = pFactory->CreateUser();
pDepartment = pFactory->CreateDepartment();
pUser->Insert();
pUser->GetUser();
pDepartment->Insert();
pDepartment->GetDepartment();
return 0;
}

测试结果

工厂模式

工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

C++中的工厂方法通常是虚函数并且常常是纯虚函数

UML图

例子:

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
/* 让每个区域风味的披萨工厂继承基类披萨工厂
* 使每个工厂的订单系统不变
* 然后创建自己风味的披萨
* 这样真正选购披萨类型,使用具体的披萨工厂决定
*/
#include <iostream>
#include <string>
#include <list>
using namespace std;
// 首先创建披萨类
class Pizza
{
public:
Pizza(string nam, string doug, string sauc)
{
name = nam;
dough = doug; // 生面团,钱
sauce = sauc; // 酱料
}
void addTops(string tops)
{
toppings.push_back(tops);
}
void prepare()
{
cout << "Preparing" << name << endl;
cout << "Tossing dough" << endl;
cout << "Adding sauce" << endl;
cout << "Adding toppings" << endl;
list<string>::iterator iter = toppings.begin();
for (; iter != toppings.end(); ++iter) {
cout << " " << *iter;
}
cout << endl;
}
void bake()
{
cout << "Bake for 25 minutes at 350" << endl;
}
void cut()
{
cout << "Cutting the pizza into diagonal slices" << endl;
}
void box()
{
cout << "Place pizza in offical PizzaStore box" << endl;
}
string getName()
{
return name;
}
private:
string name;
string dough;
string sauce;
list<string> toppings;
};
// 创建纽约cheese风格的pizza类 和 纽约clam风格的pizza类
class NYStyleCheesePizza : public Pizza
{
public:
NYStyleCheesePizza(): Pizza("NY Style Sauce and Cheese Pizza", "Thin Crust Dough", "Marinara Sauce")
{
addTops("Grated Reggiano Chesse");
}
};
class NYStyleClamPizza : public Pizza
{
public:
NYStyleClamPizza(): Pizza("NY Style Sauce and Clam Pizza", "Thin Crust Dough", "Marinara Sauce")
{
addTops("Grated Clam");
}
};
// 创建基类工厂
class PizzaStore
{
public:
virtual ~PizzaStore() {}
Pizza* oderPizza(string type)
{
Pizza* pizza = createPizza(type);
pizza->prepare();
pizza->bake();
pizza->cut();
pizza->box();
return pizza;
}
virtual Pizza* createPizza(string type) {
return NULL;
}
};
//创建具体类工厂(纽约pizza工厂)
class NYPizzaStore : public PizzaStore
{
public:
Pizza* createPizza(string item)
{
if (item == "cheese")
{
return new NYStyleCheesePizza();
}
else if (item == "clam")
{
return new NYStyleClamPizza();
}
else
{
return NULL;
}
}
};
// ... 创建其他地区工厂
int main()
{
PizzaStore* nyStore = new NYPizzaStore();
Pizza* pizza = nyStore->oderPizza("cheese");
cout << "Ethan prdered a" << pizza->getName() << endl;
return 0;
}

测试结果:

原型模式

用原型实例指定对象的种类通过拷贝这些原型创建新的对象

UML图

普通方法的实现:

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
#include <iostream>
using namespace std;
// 丑角
class Stooge { // 还是换回自己的埃及习惯吧~
public:
virtual void slapStick() = 0;
};
class Larry : public Stooge {
public:
void slapStick() {
cout << "Larry: poke eyes" << endl;
}
};
class Moe : public Stooge {
public:
void slapStick() {
cout << "Moe: slap head" << endl;
}
};
class Curly : public Stooge {
public:
void slapStick() {
cout << "Curly: suffer abuse" << endl;
}
};
int main(void) {
Stooge* roles[10];
int in, j, i = 0; // 注意初始化顺序,小心segmentation fault
cout << "L(1) M(2) C(3) Go(0): ";
cin >> in;
while (in) {
if (in == 1) {
roles[i++] = new Larry;
}
else if (in == 2) {
roles[i++] = new Moe;
}
else {
roles[i++] = new Curly;
}
cout << "L(1) M(2) C(3) Go(0): ";
cin >> in;
}
for (j = 0; j < i; j++) {
roles[j]->slapStick();
}
for (j = 0; j < i; j++) {
delete roles[j];
}
return 0;
}
// L(1) M(2) C(3) Go(0): 1
// L(1) M(2) C(3) Go(0): 2
// L(1) M(2) C(3) Go(0): 3
// L(1) M(2) C(3) Go(0): 1
// L(1) M(2) C(3) Go(0): 0
// Larry: poke eyes
// Moe: slap head
// Curly: suffer abuse
// Larry: poke eyes

测试结果:

原型模式实现:

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
#include <iostream>
using namespace std;
class Stooge {
public:
virtual Stooge* clone() = 0;
virtual void slapStick() = 0;
};
class Factory {
public:
static Stooge* create(int i);
private:
static Stooge* prototypes_[4];
};
class Larry : public Stooge {
public:
Stooge* clone() { return new Larry; }
void slapStick() {
cout << "Larry: poke eyes" << endl;
}
};
class Moe : public Stooge {
public:
Stooge* clone() { return new Moe; }
void slapStick() {
cout << "Moe: slap head" << endl;
}
};
class Curly : public Stooge {
public:
Stooge* clone() { return new Curly; }
void slapStick() {
cout << "Curly: suffer abuse" << endl;
}
};
Stooge* Factory::prototypes_[] = { 0, new Larry, new Moe, new Curly };
Stooge* Factory::create(int i) { return prototypes_[i]->clone(); }
int main() {
Stooge* roles[10];
int in, j, i = 0;
cout << "vlh L(1) M(2) C(3) Go(0): ";
cin >> in;
while (in) {
roles[i++] = Factory::create(in);
cout << "L(1) M(2) C(3) Go(0): ";
cin >> in;
}
for (j = 0; j < i; j++) {
roles[j]->slapStick();
}
for (j = 0; j < i; j++) {
delete roles[j];
}
return 0;
}

测试结果:

另一个例子:

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
// 原型模式的demo
#include <iostream>
using namespace std;
enum imageType { LSAT, SPOT };
class Image {
public:
virtual void draw() = 0;
static Image* findAndClone(imageType);
protected:
virtual imageType returnType() = 0;
virtual Image* clone() = 0;
// 注册它的原型作为图像的每个子类的声明
static void addPrototype(Image* image) {
_prototypes[_nextSlot++] = image;
}
private:
static Image* _prototypes[10];
static int _nextSlot;
};
Image* Image::_prototypes[];
int Image::_nextSlot;
// client 调用这个公共静态成员函数时,它需要一个图像的子类实例
Image* Image::findAndClone(imageType type) {
for (int i = 0; i < _nextSlot; i++) {
if (_prototypes[i]->returnType() == type) {
return _prototypes[i]->clone();
}
}
}
class LandSatImage : public Image {
public:
imageType returnType() { return LSAT; }
void draw() { cout << "LandSatImage::draw " << _id << endl; }
Image* clone() { return new LandSatImage(1); }
protected:
LandSatImage(int dummy) { _id = _count++; }
private:
// 初始化机制——这将调用注册一个图像子类原型的的默认构造函数
static LandSatImage _landSatImage;
// 当静态 私有成员被初始化时才调用
LandSatImage() {addPrototype(this); }
//每个实例名义上的“状态”机制
int _id;
static int _count;
};
// 注册子类原型
LandSatImage LandSatImage::_landSatImage;
// 初始化 每个实例名义上的“状态”机制
int LandSatImage::_count = 1;
class SpotImage : public Image {
public:
imageType returnType() { return SPOT; }
void draw() { cout << "SpotImage::draw " << _id << endl; }
Image* clone() { return new SpotImage( 1 ); }
protected:
SpotImage( int dummy ) { _id = _count++; }
private:
SpotImage() { addPrototype( this ); }
static SpotImage _spotImage;
int _id;
static int _count;
};
SpotImage SpotImage::_spotImage;
int SpotImage::_count = 1;
// 模拟创建请求流
const int NUM_IMAGES = 8;
imageType input[NUM_IMAGES] = { LSAT, LSAT, LSAT, SPOT, LSAT, SPOT, SPOT, LSAT };
int main() {
Image* images[NUM_IMAGES];
// 提供一个特定的图像类型,找到合适的原型,并返回一个克隆
for (int i = 0; i < NUM_IMAGES; i++) {
images[i] = Image::findAndClone(input[i]);
}
// 证明图像对象已经被正确克隆
for (int i = 0; i < NUM_IMAGES; i++) {
images[i]->draw();
}
for (int i = 0; i < NUM_IMAGES; i++) {
delete images[i];
}
return 0;
}

测试结果:

单例模式

UML图

教科书实现

  • 静态成员函数 instance()

  • 静态的私有变量 pInstance

  • 被保护的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Singleton {
private:
static Singleton *pInstance;
protected:
Singleton() {}
public:
static Singleton* instance();
};
Singleton* Singleton::pInstance = 0;
Singleton* Singleton::instance() {
if (pInstance == 0) {
pInstance = new Singleton();
}
return pInstance;
}

懒汉模式(时间换空间)

使用类的私有静态指针变量指向类的唯一实例,并用一个公有的静态方法获取该实例

  • 指向唯一实例的私有静态指针

  • 获取这个唯一实例的公有函数,并且在需要时创建实例

  • 构造函数私有,外界不可创建该类实例

相比于教科书上的实现,构造函数由保护变为私有,直接在类中实现获取实例的公有函数:

1
2
3
4
5
6
7
8
9
10
11
12
class Singleton {
private:
Singleton() {}
static Singleton *pInstance;
public:
static Singleton *GetInstance() {
if (pInstance == 0) {
pInstance = new Singleton();
}
return pInstance;
}
};

由于pInstance变量的释放问题,即实例的析构问题存在。虽然我们可以在程序结束的时候主动调用GetInstance()并且delete它返回的指针,
但是容易忘记并且操作繁琐。

所以我们在该类中定义一个静态全局变量,因为程序在结束时,系统会自动析构所有类的全局变量,也会析构所有类静态变量成员,
就好像他们也是全局变量一样。

所以我们定义一个这样的静态变量,加入一个只调用析构函数的class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Singleton {
private:
Singleton() {}
static Singleton *pInstance;
class GC() {
if (Singleton::pInstance) {
delete Singleton::pInstance;
}
};
static GC gc; // 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数
public:
static Singleton *GetInstance() {
if (pInstance == 0) {
pInstance = new Singleton();
}
return pInstance;
}
};

饿汉模式(空间换时间)

如果你觉得上面的实现不够简洁的话,所以我们想到用局部静态方法来代替

把实例作为局部静态变量

一个考虑不周全的实现

1
2
3
4
5
6
7
8
9
class Singleton {
private:
Singleton() {};
public:
static Singleton &GetInstance() {
static Singleton instance; //局部静态变量
return instance;
}
};

之所以说上面的代码考虑不周全是因为,当我们调用静态方法get实例的时候,
如:

1
Singleton singleton = Singleton::GetInstance();

这种调用方式编译器会为类生成一个 默认复制构造函数,来支持类的拷贝。

这样的话就违背了单例的特性,所以我们改成

返回指针的形式:

1
2
3
4
5
6
7
8
9
class Singleton {
private:
Singleton() {}
public:
static Singleton* GetInstance() {
static Singleton instance;
return &instance;
}
};

或者我们重载 = 操作符,显式地声明类拷贝的复制构造函数

1
2
3
4
5
6
7
8
9
10
11
class Singleton {
private:
Singleton() {}
Singleton(const Singleton&);
Singleton &operator = (const Singleton&);
public:
static Singleton &GetInstance() {
static Singleton instance;
return instance;
}
};