设计模式
dd

设计模式是指在软件开发中,经过验证的,用于解决在特定环境下,重复出现的,特定问题的解决方案

按照设计目的分为三类:创建型模式、结构型模式和行为模式

分别对应了面向对象开发的三个问题:如何创建对象、如何组合对象、如何处理对象之间的动态通信和职责分配

设计原则

单一职责

在设计类的时候要尽量缩小粒度,使功能明确、单一,不要做多余的事情(高内聚,低耦合)

单一职责下的类会比较小而精悍,需要使用结构性模式组合复用他们

开闭原则

一个类应该对扩展开发,对修改关闭

关键是要做好封装,隐藏内部的实现细节,开发足够的接口,这样子外部的代码就只能通过接口去扩展功能,不需要侵入到类的内部

final关键字

不能说要实现某个功能必要要修改类内部的代码才能实现,需要能够通过扩展实现新的功能(低耦合)

里氏替换

子类能够完全替换父类,不会改变父类定义的行为

比如一个基类鸟类中有一个方法,能够飞行,所有的鸟类都必须继承它,但是企鹅、鸵鸟这些没办法飞行(使用接口代替继承)

接口隔离

不应该强迫客户依赖于它们不需要的接口

建立单一接口,不要建立庞大的接口,尽量细化,接口中的方法要建立少

类“犬科”依赖接口I中的方法:捕食(),行走(),奔跑(), 宠物狗类是对类“犬科”的实现。 对于具体的类:宠物狗来说,虽然存在着用不到的方法,但由于继承了接口,所以也必须要实现这些用不到的方法

依赖倒置

上层要避免依赖下层的实现细节,两者都应该依赖于抽象

比如Java的操作数据库,Java定义了一组接口,由各个数据库去实现它,Java不依赖于他们,数据库依赖于Java

组合优于继承

继承耦合度高,组合耦合度低

继承基类是固定好的,但是组合通过组合类的指针,可以传入不同的类,避免高耦合

设计模式

单例模式

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

版本一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Singleton1 {
public:
static Singleton1* GetInstance() {
if (instance == nullptr) {
instance = new Singleton1();
}
return instance;
}
private:
// 防止外界构造和删除对象
Singleton1() = default;
~Singleton1() = default;
Singleton1(const Singleton1&) = default;
Singleton1& operator=(const Singleton1&) = default;

static Singleton1 *instance;
};

Singleton1* Singleton1::instance = nullptr;

存在内存泄露问题,instance是在堆上的,需要手动释放,但是外界没办法调用delete释放对象

线程安全问题,可能会有多个线程同时分配内存

版本二

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
class Singleton2 {
public:
static Singleton2* GetInstance() {
if (instance == nullptr) {
instance = new Singleton2();
atexit(Destructor); // 在程序退出的时候释放
}
return instance;
}
private:
// 防止外界构造和删除对象
Singleton2() = default;
~Singleton2() = default;
Singleton2(const Singleton2&) = default;
Singleton2& operator=(const Singleton2&) = default;

static void Destructor() {
if (instance != nullptr) {
delete instance;
instance = nullptr;
}
}

static Singleton2 *instance;
};

Singleton2* Singleton2::instance = nullptr;

解决了内存泄露问题

版本三

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
class Singleton3 {
public:
static Singleton3* GetInstance() {
if (instance == nullptr) {
lock_guard<mutex> lock(mutex_);
if (instance == nullptr) {
instance = new Singleton3();
atexit(Destructor);
}
}
return instance;
}
private:
// 防止外界构造和删除对象
Singleton3() = default;
~Singleton3() = default;
Singleton3(const Singleton3&) = default;
Singleton3& operator=(const Singleton3&) = default;

static void Destructor() {
if (instance != nullptr) {
delete instance;
instance = nullptr;
}
}

static Singleton3 *instance;
static mutex mutex_;
};

Singleton3* Singleton3::instance = nullptr;

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
class Singleton4 {
public:
static Singleton4* GetInstance() {
Singleton4* tmp = instance.load(std::memory_order_relaxed);
atomic_thread_fence(std::memory_order_acquire); // 获取内存屏障
if (tmp == nullptr) {
lock_guard<mutex> lock(mutex_);
tmp = instance.load(memory_order_relaxed);
if (tmp == nullptr) {
tmp = new Singleton4;
atomic_thread_fence(std::memory_order_release); // 释放内存屏障
instance.store(tmp,memory_order_relaxed);
atexit(Destructor);
}
}
return tmp;
}
private:
// 防止外界构造和删除对象
Singleton4() = default;
~Singleton4() = default;
Singleton4(const Singleton4&) = default;
Singleton4& operator=(const Singleton4&) = default;

static void Destructor() {
if (instance != nullptr) {
delete instance;
instance = nullptr;
}
}

static atomic<Singleton4*> instance;
static mutex mutex_;
};

atomic<Singleton4*> Singleton4::instance;
mutex Singleton4::mutex_;

版本五

c++11 magic static 特性:如果当变量在初始化的时候,并发同时进⼊声明语句,并发线程将 会阻塞等待初始化结束

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

版本六

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
template<typename T>
class Singleton6 {
public:
static T& GetInstance() {
static T instance;
return instance;
}
protected:
virtual ~Singleton6() = default;
Singleton6() = default;
Singleton6(const Singleton6&) = default;
Singleton6& operator=(const Singleton6&) = default;
};

class DesignPattern : public Singleton6<DesignPattern> {
friend class Singleton6<DesignPattern>;
public:
~DesignPattern() = default;

private:
DesignPattern() = default;
DesignPattern(const DesignPattern&) = default;
DesignPattern& operator=(const DesignPattern&) = default;
};

int main() {
DesignaPattern &d1 = new DesignaPattern::getInstance();
}

模板方法

定义一个操作中的算法的骨架 ,但将一些步骤延迟到子类中。 Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤

本质:通过固定算法骨架约束子类的行为

理解:只是定义了一个模板是什么样子,但是具体怎么实现交给子类去实现

背景

某个品牌动物园,有一套固定的表演流程,但是其中有若干个表演子流程可创新替换,以尝试迭代 更新表演流程

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

using namespace std;

class Zoo {
public:
void show() {
show1();
show2();
show3();
}

protected:
virtual void show1() {
cout << "Zoo:show1()" << endl;
}
virtual void show2() {
cout << "Zoo:show2()" << endl;
}
virtual void show3() {
cout << "Zoo:show3()" << endl;
}
};
class Zoo1 : public Zoo {
private:
virtual void show1() {
cout << "Zoo1:show0()" << endl;
}
virtual void show2() {
cout << "Zoo1:show1()" << endl;
}
virtual void show3() {
cout << "Zoo1:show3()" << endl;
}
};

int main() {
Zoo* zoo = new Zoo1;
zoo->show();
return 0;
}

观察者模式

定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新

要求:

  • 独立地改变目标与观察者,从而使二者之间的关系松耦合
  • 观察者自己决定是否订阅通知,目标对象并不关注谁订阅了
  • 观察者不要依赖通知顺序,目标对象也不知道通知顺序

背景

气象站发布气象资料给数据中心,数据中心经过处理,将气象信息更新到两个不同的显示终端(A 和B);

上层:气象站

下层:显示中断

依赖:数据中心

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
#include <iostream>
#include <vector>
using namespace std;

class IDisplay {
public:
virtual void show(float temperature) = 0;
virtual ~IDisplay() = default;
};

class IDisplayA : public IDisplay {
public:
virtual void show(float temperature);
};

class IDisplayB : public IDisplay {
public:
virtual void show(float temperature);
};

class WeatherData {
};

class DataCenter {
public:
void Attach(IDisplay *ob);
void Detach(IDisplay *ob);
void Notify() {
float temper = CalcTemperature();
for (auto iter = obs.begin(); iter != obs.end(); iter++) {
(*iter)->show(temper);
}

}
private:
virtual WeatherData* GetWeatherData();

virtual float CalcTemperature() {
WeatherData *data = GetWeatherData();
float temper;
return temper;
}

vector<IDisplay*> obs;
};

int main() {
DataCenter *center = new DataCenter;
IDisplay *da = new IDisplayA;
IDisplay *db = new IDisplayB;
center->Attach(da);
center->Attach(db);
center->Notify();
}

策略模式

定义一系列算法,把它们一个个封装起来,并且使它们可互相替换。该模式使得算法可独立于使用它的客户程序而变化

要点:

策略模式提供了一系列可重用的算法,从而可以使得类型在运⾏时方便地根据需要在各个算法之间 进行切换(分离算法,选择实现)

策略模式消除了条件判断语句;也就是在解耦合

背景

某商场节假日有固定促销活动,为了加大促销力度,现提升国庆节促销活动规格

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
class Context {

};

class ProStategy {
public:
virtual double CalcPro(const Context &ctx) = 0;
virtual ~ProStategy() = default;
};

class VAC_Spring : public ProStategy {
public:
virtual double CalcPro(const Context &ctx) {}
};

class VAC_QIXI : public ProStategy {
public:
virtual double CalcPro(const Context &ctx) {}
};

class Promotion {
public:
Promotion(ProStategy *sss) : s(sss) {}
~Promotion(){}
double CalcPromotion(const Context &ctx) {
return s->CalcPro(ctx);
}
private:
ProStategy *s;
};

int main() {
Context ctx;
ProStategy *s = new VAC_QIXI();
ProStategy *s1 = new VAC_Spring();
Promotion *p = new Promotion(s);
p->CalcPromotion(ctx);
p = new Promotion(s1);
p->CalcPromotion(ctx);

return 0;
}

工厂方法

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟到子类

场景:

  • 比如连接池、线程池
  • 隐藏对象真实类型
  • 对象创建会有很多参数来决定如何创建
  • 创建对象有复杂的依赖关系

背景

实现一个导出数据的接口,让客户选择数据的导出方式

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
class IExport {

};

class ExportXml : public IExport {

};

class IExportFactory {
public:
virtual IExport *newExport() = 0;
};

class ExportXmlFactory : public IExportFactory {
public:
IExport *NewExport() {
IExport *temp = new ExportXml;
return temp;
}
};

class ExportData {
public:
ExportData(IExportFactory *factory) : factory(factory) {}
~ExportData() {
if (factory) {
delete factory;
factory = nullptr;
}
}
bool Export(const std::string &data) {
IExport *e = factory->newExport();
e->Export(data);
}
private:
IExportFactory *factory;
};

int main() {
ExportData ed(new ExportXmlFactory);
ed.Export("hello");
}

将工厂传递给需要的对象,又工厂去创建对象,传入不同的工厂就可以创建不同的对象

抽象工厂

提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类

背景

如果业务涉及的类越来越多,使用工厂方法每一个子类都需要对应一个工厂,太复杂

业务中需要创建口罩、防毒面具、防护服这三种产品,而每一种产品有包含高端和低端两类,按照工厂方法模式的解决方案需要创建六个工厂

image

可以对类进行分类,将口罩分为高端以及低端,每组中的不同产品,由同一个工厂类的不同方法创建

image

代码实现简化为有口罩和防护服两个抽象接口,分别拥有高端和低端两个实现类

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

using namespace std;

// 产品
class IMask {
public:
virtual void showMask() = 0;
};

class LowMask : public IMask {
public:
virtual void showMask() {
cout << "低端口罩" << endl;
}
};

class HighMask : public IMask {
public:
virtual void showMask() {
cout << "高端口罩" << endl;
}
};

class ISuit {
public:
virtual void showSuit() = 0;
};

class LowSuit : public ISuit {
public:
virtual void showSuit() {
cout << "低端防护服" << endl;
}
};

class HighSuit : public ISuit {
public:
virtual void showSuit() {
cout << "高端防护服" << endl;
}
};

// 工厂
class IFactory {
public:
virtual IMask* createMask() = 0;
virtual ISuit* createSuit() = 0;
};

class LowFactory : public IFactory {
public:
virtual IMask* createMask() {
IMask *mask = new LowMask();
return mask;
}
virtual ISuit* createSuit() {
ISuit* suit = new LowSuit();
return suit;
}
};

class HightFactory : public IFactory {
public:
virtual IMask* createMask() {
IMask* mask = new HighMask();
return mask;
}
virtual ISuit* createSuit() {
ISuit* suit = new HighSuit();
return suit;
}
};

int main() {
IFactory* factory1 = new LowFactory();
IFactory* factory2 = new HightFactory();

IMask *mask1 = factory1->createMask();
IMask *mask2 = factory2->createMask();
ISuit *suit1 = factory1->createSuit();
ISuit *suit2 = factory2->createSuit();

mask1->showMask();
mask2->showMask();
suit1->showSuit();
suit2->showSuit();

return 0;
}

也就是说将产品分类,然后不同类有不同的工厂,在工厂里面生产不同的商品

区别

简单工厂模式:

简单工厂模式有唯一的工厂类,工厂类的创建方法根据传入的参数做if-else条件判断,决定最终创建什么样的产品对象

(需要修改代码,违法了开闭原则)

工厂方法模式:

工厂方法模式由多个工厂类实现工厂接口,利用多态来创建不同的产品对象,从而避免了冗长的if-else条件判断

抽象工厂模式:

抽象工厂模式把产品子类进行分组,同组中的不同产品由同一个工厂子类的不同方法负责创建,从而减少了工厂子类的数量