创建型模式

设计模式之创建型模式

Factory Method

关于工厂模式的一个比较好的介绍:工厂设计模式有什么用? - 郭小成的回答 - 知乎

解决了硬编码

一个类如果发生了变动,假使你使用了硬编码(直接new),那么你还得找到所有new的地方一个一个更改

而将new的过程放入Factory中,那么你只用改变Factory就行了

Abstract Factory

抽象工厂模式提供了一种方式,可以将一组具有同一主题的单独的工厂封装起来(如果不把同一主题的封装起来,而是一味使用Factory Method,代码量会急剧膨胀)

在正常使用中,客户端程序需要创建抽象工厂的具体实现,然后使用抽象工厂作为接口来创建这一主题的具体对象

不同的theme,控件的观感也不同

我们可以定义一个ThemeFactory作为base class(interface)

然后通过继承(实现)衍生出各种(derived)class以此来实现不同的theme

每一种theme都有不同风格的widget,我们将其称之为factoryproduct

1
2
3
4
5
6
class ThemeFactory {
public:
makeWindows(); //这些就是product
makeMenuBar();
make.........;
}

然后关于Abstract Factory
优点是

  • 分离了具体的类
  • 使得便于交换产品的系列(通过创建不同的derived class)
  • 它有利于产品的一致性(每一个derived class所产出的产品都是一一致的)

缺点是

  • 难以加入新种类的产品(当base class加入新产品时,derived class的也要加入新的产品,这就要修改大量的derived class)
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
#include <iostream>
class Windows{};
class windowsMacStyle : public Windows{};
class windowsLinuxStyle : public Windows();

class ThemeFactory { //base class确保了产品的一致性
public:
//factory method
virtual void makeWindows() = 0;
};

class MacThemeFactory : public ThemeFactory {
public:
virtual void makeWindows() {
std::cout << "this is a windows which is mac style" << std::endl;
//return new windowsMacStyle();
}
};

class LinuxThemeFactory : public ThemeFactory {
public:
virtual void makeWindows() {
std::cout << "this is a windows which is linux style" << std::endl;
//return new windowsLinuxStyle();
}
};

int main() {
ThemeFactory *test = new LinuxThemeFactory();
test->makeWindows();
delete test;

test = new MacThemeFactory(); //改变产品的系列
test->makeWindows();
delete test;

return 0;
}

Builder

Builder模式由Director和Builder组成

虽然名为Builder,但是Director才是重点

将一个复杂对象的构建和它的表示分离,使得同样的创建过程可有不同的表示
效果:

  • 可以改变一个产品的内部表示
  • 将构建代码和表示代码分开
  • 可以对构建过程有更加精细的控制

相对于Abstract Factory,Builder重视的是一个构建的过程(direct),而Abstract Factory只是提供零件的过程

可以这么认为,Abstract Facotry套上Builder模式也可以产生一个新的Builder( director + builder )

想构建不同的产品?新增builder就好

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
#include <iostream>
#include <map>
#include <string>

class Computer {
private:
std::map<std::string, std::string> computer;
public:
void show() {
auto iter = computer.begin();
while (iter != computer.end()){
std::cout << iter->first << ":" << iter->second << std::endl;
iter++;
}
}
void set(std::string type, std::string product) {
computer[type] = product;
}
};

class Builder {
public:
virtual void BuildCPU() = 0;
virtual void BuildMainboard() = 0;
virtual void BuildHD() = 0;
virtual Computer getComputer() = 0;
};

class BuilderA : public Builder {
private:
Computer compter;
public:
virtual void BuildCPU() {
std::cout << "add intel CPU" << std::endl;
compter.set("CPU", "Inter");
}

virtual void BuildMainboard() {
std::cout << "add MSI MainBoard" << std::endl;
compter.set("MainBoard", "MSI");
}

virtual void BuildHD() {
std::cout << "add hgst HD" << std::endl;
compter.set("HD", "hgst");
}

virtual Computer getComputer() {
return compter;
}
};

class BuilderB : public Builder {
private:
Computer compter;
public:
virtual void BuildCPU() {
std::cout << "add AMD CPU" << std::endl;
compter.set("CPU", "AMD");
}

virtual void BuildMainboard() {
std::cout << "add ROG MainBoard" << std::endl;
compter.set("MainBoard", "ROG");
}

virtual void BuildHD() {
std::cout << "add SAMSUMG HD" << std::endl;
compter.set("HD", "SAMSUMG");
}

virtual Computer getComputer() {
return compter;
}
};

class Director {
public:
void Construct(Builder *builder) {
builder->BuildCPU();
builder->BuildMainboard();
builder->BuildHD();
}
};

int main() {
Builder *builder = new BuilderA();
Director p;

std::cout << "construct the computer" << std::endl;
p.Construct(builder); //指派一个装配人员(此处可以复合Abstract Factory)

Computer computer = builder->getComputer();
std::cout << "\nShow computer" << std::endl;
computer.show();

delete builder;
return 0;
}

Prototype

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

原型模式是创建型模式的一种,其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的。

原型模式多用于创建复杂的或者耗时的实例,因为这种情况下,复制一个已经存在的实例使程序运行更高效;或者创建值相等,只是命名不一样的同类数据。

Prototype模式的核心就是,复制整体,修改部分,形成一个新的实例

举个不是特别恰当的例子

例如Jack需要多份简历,简历大部分都是相同的,只有工作经验不同

因此,只需要复制一份之前的简历,再修改一下工作经验即可

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Resume {
private:
std::string Name;
int Age;
std::string Experience;
public:
Resume(std::string name, int age, std::string experience);
Resume * Clone() {return new Resume(*this);};
void SetPersonalInfo();
void SetExperience(std::string expr);
};

int main() {
Resume *resume1 = new Resume("Jack", 26, "Work on A Company");
Resume *resume2 = resume1->Clone();
resume2->SetExperience("Work on B Company")
return 0;
}

可能这个例子不是特别能说明原型模式的好处,但是如果在构造时需要设置多个属性(例如20个)

但是大部分属性都是相同,只需要修改部分属性,那么原型模式的好处就会大大体现出来

SingleTon

保证一个类只有一个实例,并提供一个访问它的全局访问点

对于一些类来说,只有一个实例是很重要的

例如:打印机,数据库等

SingleTon的效果是

  • 对唯一实例的受控访问
  • 缩小名空间,放置全局变量污染
  • 允许对操作和表示的精化
  • 允许可变数目的实例
  • 比类操作更灵活

实现:

如何保证一个唯一的实例?

可以将创建实例的操作放在一个类操作后面

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Singleton {
private:
static Singleton* _instance;
protected:
Singleton(){};
public:
static Singleton * Instance();
};

Singleton * Singleton::_instance = nullptr;

Singleton * Singleton::Instance(){
if (!_instance)
_instance = new Singleton;
return _instance;
}