创建型模式

创建型模式5个

单例模式(Singleton)、抽象工厂模式(Abstract Factory)、建造者模式(Builder)、工厂模式(Factory)、原型模式(Prototype)

简单工厂模式(Simple Factory Pattern)

简单工厂模式(Simple Factory Pattern)属于创建型模式。简单工厂模式,由一个工厂对象决定创建出哪一种产品类的实例,它经常被用于封装创建对象的代码。
简单工厂模式

  • SimpleFactory:简单工厂类,简单工厂模式的核心,它负责实现创建所有实例。简单工厂创建产品的方法可以被外界直接调用来创建所需的产品对象。
  • IProduct:抽象产品类,简单工厂模式创建的所有对象的父类,它负责描述所有实例所共有的公共函数接口。
  • ConcreteProduct:具体产品类,是简单工厂模式的创建目标。

假如比萨店现在需要建立一套点餐系统,需要将客户点的比萨加入到账单中。比萨店向客户推出了不同口味的比萨,有蛤蜊披萨(Clam Pizza)、素食披萨(Veggie Pizza)和芝士比萨(Cheese Pizza)等。这就用到了简单工厂模式。下面以C++来实现简单工厂模式:

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
// 创建抽象产品类Pizza:
class Pizza {
public:
//纯虚函数(抽象函数接口)
virtual string getDescription() = 0;
};
// 创建具体产品类。接着我们创建各个口味的比萨,它们都继承了抽象父类Pizza ,并实现了父类的getDescription方法。
//蛤蜊比萨
class ClamPizza :public Pizza {
string name="蛤蜊比萨";
public:
string getDescription() {
return name + " 价格45元";
}
};
//素食比萨
class VeggiePizza :public Pizza {
string name = "素食比萨";
public:
string getDescription() {
return name+ " 价格26元";;
}
};
// 创建简单工厂类。接下来创建一个简单工厂类,它提供了一个静态方法createPizza用来生产比萨。你只需要传入你想生产的比萨名称,它就会实例化相应比萨对象
enum PizzaName {CLAM,VEGGIE,CHEESE};
class PizzaFactory {
public:
static Pizza* createPizza(PizzaName name) {
Pizza* pizza = nullptr;
switch (name){
case CLAM:
pizza = new ClamPizza();
break;
case VEGGIE:
pizza = new VeggiePizza();
break;
case CHEESE:
pizza = new CheesePizza();
break;
}
return pizza;
}
};
// 客户端调用工厂类。客户端调用简单工厂类,传入不同口味比萨的名称生产出比萨并调用该对象getDescription()
int main() {
cout<< PizzaFactory::createPizza(CLAM)->getDescription() << endl;
cout<< PizzaFactory::createPizza(VEGGIE)->getDescription() << endl;
cout<< PizzaFactory::createPizza(CHEESE)->getDescription() << endl;
}

使用场景:

  • (1)工厂类负责创建的对象比较少。
  • (2)客户只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心。

工厂方法模式(Factory Method Pattern)

工厂方法模式(Factory Method Pattern)属于创建型模式,定义一个创建对象的接口函数,但由子类决定实例化某一个类,让工厂类把实例化推迟到子类。

工厂方法模式

  • 基类:抽象产品类。
  • 子类:具体产品类。
  • 工厂类:即抽象工厂类,提供了一个创建对象的方法,也称为“工厂方法”,该方法返回一个具体产品类的对象。
  • 子类工厂:具体工厂类,实现抽象工厂类的“工厂方法”,来创建某个具体产品类实例。每一个子类工厂,负责创建一个具体产品类的对象。

还是以生产比萨为例,比萨店需要根据客户订单,生产不同口味的比萨。这里比萨生产不是交由一个厨房生产,而是不同口味比萨交由不同厨房来专门制作。

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
// 实现我们的抽象比萨类和具体口味的比萨。
class Pizza {
public:
virtual string getDescription() = 0;
};
//蛤蜊比萨
class ClamPizza :public Pizza {
string name="蛤蜊比萨";
public:
string getDescription() {
return name + " 价格45元";
}
};
//素食比萨
class VeggiePizza :public Pizza {
string name = "素食比萨";
public:
string getDescription() {
return name+ " 价格26元";;
}
};
// 抽象工厂和制作不同口味比萨的厨房
//抽象工厂,提供创建创建对象的接口createPizza()
class PizzaFactory {
public:
virtual Pizza* createPizza() = 0;
};
//具体工厂类,创建蛤蜊比萨
class ClamPizzaFactory :PizzaFactory {
public:
virtual Pizza* createPizza() {
return new ClamPizza;
}
};
//具体工厂类,创建素食比萨
class VeggiePizzaFactory :public PizzaFactory{
public:
virtual Pizza* createPizza() {
return new VeggiePizza;
}
};
//具体工厂类,创建芝士比萨
class CheesePizzaFactory :public PizzaFactory {
public:
virtual Pizza* createPizza() {
return new CheesePizza;
}
};

简单工厂和工厂方法模式的比较

  • 简单工厂模式:专门定义一个工厂类负责创建其他类的实例,最大的优点在于工厂类中包含了必要的逻辑,根据客户需要的条件动态实例化相关的类。其缺点就是生产新产品时需要更改简单工厂类的代码,这违背了开放封闭原则。

  • 工厂方法模式:提供创建对象的接口,让子类去决定具体实例化的对象,把简单的内部逻辑判断移到了客户端代码。工厂方法克服了简单工厂违背开放封闭原则的缺点,又保持了封装对象创建过程的优点。

抽象工厂模式(Abstract Factory Pattern)

抽象工厂模式(Abstract Factory Pattern)属于创建型模式,为创建一组相关或者相互依赖的对象(产品族)提供一个抽象类接口,而无需指定它们的具体类。产品族的定义是:某个具体工厂生产的所有类型的产品,比如定义了一个抽象工厂接口A,它可以生产三种产品:p1、p2、p3,而这三个产品就叫一个产品族。

抽象工厂模式

  • AbstractFactory:抽象工厂类,它声明了用来创建不同产品的方法。
  • ConcreteFactory:具体工厂类,实现抽象工厂中申明的创建产品的方法。
  • AbstractProduct:抽象产品类,为每种产品声明抽象描述方法。比如上图的AbstractProductA和 AbstractProductB。
  • ConcreteProduct:具体产品类,定义具体工厂生产的具体产品,并实现抽象产品类中申明的抽象描述方法。
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
// 首先定义抽象产品类,分别为面团Dough和蛤蜊Clam:
//面团
class Dough{
public:
virtual string getDescription() = 0;
};
//蛤蜊
class Clam{
public:
virtual string getDescription() = 0;
};
// 实现纽约和芝加哥两地比萨店使用的不同的具体原料类:
//纽约面团
class NewYorkDough:public Dough{
public:
virtual string getDescription() {
return "纽约薄面团";
};
};
//纽约蛤蜊
class NewYorkClam:public Clam{
public:
virtual string getDescription() {
return "纽约新鲜蛤蜊";
};
};
//芝加哥面团
class ChicagoDough:public Dough{
public:
virtual string getDescription() {
return "芝加哥厚面团";
}
};
//芝加哥面团
class ChicagoClam:public Clam{
public:
virtual string getDescription() {
return "芝加哥冷冻蛤蜊";
}
};
// 抽象产品类和具体产品类,现在来完成抽象工厂和具体工厂的设计和实现。
//抽象工厂
class AbstractFactory {
public:
virtual Dough* createDough() = 0;
virtual Clam* createClam() = 0;
};
//具体生产纽约比萨原料工厂
class NewYorkFactory :public AbstractFactory {
public:
Dough* createDough() {
return new NewYorkDough;
}
Clam* createClam() {
return new NewYorkClam;
}
};
//具体生产芝加哥比萨原料工厂
class ChicagoFactory :public AbstractFactory {
public:
Dough* createDough() {
return new ChicagoDough;
}
Clam* createClam() {
return new ChicagoClam;
}
};

应用场景:

  • (1)一个系统不依赖于产品族实例如何被创建、组合和表达的细节。如本文例子中一个比萨店生产的比萨原料是一个产品族。
  • (2)系统中有多于一个产品族。一个产品族存在存在着多个抽象类,如蛤蜊基类、面团基类等。并且产品族中分属各个抽象基类的各个实现类之间存在着一定的关联或者约束,就可以使用抽象工厂模式。假如各个抽象类的实现类之间不存在关联或约束,则使用多个独立的工厂来对产品进行创建,则更合适一点

单例模式(Singleton Pattern)

单例模式(Singleton Pattern),确保一个类只有一个实例,并提供一个全局访问点。

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
// 急切式, 这种方式在进入main函数前就完成了类对象的定义,避免了多线程的同步问题,但是没有做到需要类对象时才定义,没有达到“懒惰实例化”的效果
class Singleton {
private:
Singleton(){} //构造函数私有
static Singleton pInstance;
public:
static Singleton* getInstance(){
return &pInstance;
}
};
Singleton Singleton::pInstance; //定义静态类对象
// 双重检查加锁式
HANDLE hMutex;
hMutex = CreateMutex( NULL,FALSE,NULL);
class Singleton{
private:
Singleton(){} //构造函数私有
static Singleton* pInstance;
public:
static Singleton* getInstance(){
if (pInstance == NULL){ //判断是否第一次调用
WaitForSingleObject(hMutex, INFINITE); //上锁
if (pInstance == NULL)
pInstance = new Singleton();
ReleaseMutex(hMutex); //解锁
}
return pInstance;
}
};
Singleton* Singleton::pInstance=NULL;
// 局部静态变量式,如果getInstance()函数返回的是类对象引用,会出现类拷贝的问题,这就违背了单例的特性。产生这个问题原因在于:编译器会为类生成一个默认的拷贝构造函数。
class Singleton{
private:
Singleton(){} //构造函数私有
Singleton(const Singleton&)=delete; //禁止拷贝构造函数
public:
static Singleton& getInstance(){
static Singleton instance;
return instance;
}
};

建造者模式

建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式

  • 产品类:一般是一个较为复杂的对象,也就是说创建对象的过程比较复杂,一般会有比较多的代码量。在本类图中,产品类是一个具体的类,而非抽象类。实际编程中,产品类可以是由一个抽象类与它的不同实现组成,也可以是由多个抽象类与他们的实现组成。
  • 抽象建造者:引入抽象建造者的目的,是为了将建造的具体过程交与它的子类来实现。这样更容易扩展。一般至少会有两个抽象方法,一个用来建造产品,一个是用来返回产品。
  • 建造者:实现抽象类的所有未实现的方法,具体来说一般是两项任务:组建产品;返回组建好的产品。
  • 导演类:负责调用适当的建造者来组建产品,导演类一般不与产品类发生依赖关系,与导演类直接交互的是建造者类。一般来说,导演类被用来封装程序中易变的部分。

对于客户来说,只需知道导向者就可以了,通过导向者,客户就能构造复杂的对象,而不需要知道具体的构造过程。下面给出小人例子的代码实现。

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
class Builder
{
public:
virtual void BuildHead() {}
virtual void BuildBody() {}
virtual void BuildLeftArm(){}
virtual void BuildRightArm() {}
virtual void BuildLeftLeg() {}
virtual void BuildRightLeg() {}
};
//构造瘦人
class ThinBuilder : public Builder
{
public:
void BuildHead() { cout<<"build thin body"<<endl; }
void BuildBody() { cout<<"build thin head"<<endl; }
void BuildLeftArm() { cout<<"build thin leftarm"<<endl; }
void BuildRightArm() { cout<<"build thin rightarm"<<endl; }
void BuildLeftLeg() { cout<<"build thin leftleg"<<endl; }
void BuildRightLeg() { cout<<"build thin rightleg"<<endl; }
};
//构造胖人
class FatBuilder : public Builder
{
public:
void BuildHead() { cout<<"build fat body"<<endl; }
void BuildBody() { cout<<"build fat head"<<endl; }
void BuildLeftArm() { cout<<"build fat leftarm"<<endl; }
void BuildRightArm() { cout<<"build fat rightarm"<<endl; }
void BuildLeftLeg() { cout<<"build fat leftleg"<<endl; }
void BuildRightLeg() { cout<<"build fat rightleg"<<endl; }
};
//构造的指挥官
class Director
{
private:
Builder *m_pBuilder;
public:
Director(Builder *builder) { m_pBuilder = builder; }
void Create(){
m_pBuilder->BuildHead();
m_pBuilder->BuildBody();
m_pBuilder->BuildLeftArm();
m_pBuilder->BuildRightArm();
m_pBuilder->BuildLeftLeg();
m_pBuilder->BuildRightLeg();
}
};

与工厂模式相比,建造者模式一般用来创建更为复杂的对象,因为对象的创建过程更为复杂,因此将对象的创建过程独立出来组成一个新的类——导演类。也就是说,工厂模式是将对象的全部创建过程封装在工厂类中,由工厂类向客户端提供最终的产品;而建造者模式中,建造者类一般只提供产品类中各个组件的建造,而将具体建造过程交付给导演类。由导演类负责将各个组件按照特定的规则组建为产品,然后将组建好的产品交付给客户端。

原型模式

用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
原型模式
原型模式实现的关键就是实现Clone函数,对于C++来说,其实就是拷贝构造函数,需实现深拷贝,下面给出一种实现。

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
//父类
class Resume
{
protected:
char *name;
public:
Resume() {}
virtual ~Resume() {}
virtual Resume* Clone() { return NULL; }
virtual void Set(char *n) {}
virtual void Show() {}
};
class ResumeA : public Resume
{
public:
ResumeA(const char *str); //构造函数
ResumeA(const ResumeA &r); //拷贝构造函数
~ResumeA(); //析构函数
ResumeA* Clone(); //克隆,关键所在
void Show(); //显示内容
};
ResumeA::ResumeA(const char *str)
{
if(str == NULL) {
name = new char[1];
name[0] = '\0';
}
else {
name = new char[strlen(str)+1];
strcpy(name, str);
}
}
ResumeA::~ResumeA() { delete [] name;}
ResumeA::ResumeA(const ResumeA &r) {
name = new char[strlen(r.name)+1];
strcpy(name, r.name);
}
ResumeA* ResumeA::Clone() {
return new ResumeA(*this);
}
void ResumeA::Show() {
cout<<"ResumeA name : "<<name<<endl;
}