update some pattern md.
parent
94dcc35303
commit
4fe9c33dc8
|
@ -1 +1,208 @@
|
||||||
# 人靠衣装,美靠靓装——装饰模式,教你成为最靓的仔~
|
# 人靠衣装,美靠靓装——装饰模式,教你成为最靓的仔~
|
||||||
|
```
|
||||||
|
成都的气温说降就降啊!还没立冬呢,温度骤然下降,只有10度了。扛不住了,Jungle要穿一件毛衣!
|
||||||
|
额,出门还是很冷!那再带个围巾!
|
||||||
|
天,上半身不冷了,下半身还那么冷~~~穿上秋裤!
|
||||||
|
出门前想了想,这种天气骑摩拜单车一定吹着手冷,于是Jungle又带上了手套……
|
||||||
|
看到了吗,天冷了,Jungle想加什么衣裤即加什么衣裤,各种装饰让Jungle不再感到寒冷。不过,不管Jungle穿再多衣服,Jungle始终还是那个Jungle,那个生活在成都的狗子,这一点是不变的!
|
||||||
|
```
|
||||||
|
|
||||||
|
看到上面的例子了吗?这就是装饰模式的典型应用,**穿戴再多服饰并不会改变Jungle的本质,但穿戴会让Jungle更加温暖和帅气,满足Jungle冷天出行的需求**。在软件设计中,对已有对象的功能进行扩展,以获得更加符合用户需求的对象,使得对象具有更加强大的功能,这就是装饰模式。
|
||||||
|
|
||||||
|
## 1.装饰模式简介
|
||||||
|
装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为,比如手机,为防止摔坏,可以给手机贴膜或者带上保护套;为美观,可以在保护套上贴卡通贴纸;为便于携带可以增加挂绳,如下图。但这并不改变手机的本质。
|
||||||
|
![avatar](https://github.com/FengJungle/DesignPattern/blob/master/10.DecoratorPattern/1.Picture/%E8%A3%85%E9%A5%B0%E6%A8%A1%E5%BC%8F%E5%AE%9E%E4%BE%8B%E5%9B%BE.png)
|
||||||
|
|
||||||
|
```
|
||||||
|
装饰模式:
|
||||||
|
动态地给一个对象增加一些额外的职责。就扩展功能而言,装饰模式提供了一种比使用子类更加灵活的替代方案。
|
||||||
|
```
|
||||||
|
装饰模式是一种用于替代继承的技术。通过一种无须定义子类的方式给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系。装饰模式中引入了装饰类,在装饰类中既可以调用待装饰的原有对象的方法,还可以增加新的方法,以扩充原有类的功能。
|
||||||
|
|
||||||
|
## 2.装饰模式结构
|
||||||
|
装饰模式的结构如图所示:
|
||||||
|
![avatar](https://github.com/FengJungle/DesignPattern/blob/master/10.DecoratorPattern/1.Picture/%E8%A3%85%E9%A5%B0%E6%A8%A1%E5%BC%8FUML%E5%9B%BE.png)
|
||||||
|
|
||||||
|
装饰模式中有如下角色:
|
||||||
|
|
||||||
|
- Component(抽象构件):是具体构件类和装饰类的共同基类,声明了在具体构件中定义的方法,客户端可以一致的对待使用装饰前后的对象;
|
||||||
|
- ConcreteComponent(具体构件):具体构件定义了构件具体的方法,装饰类可以给它增加更多的功能;
|
||||||
|
- Decorator(抽象装饰类):用于给具体构件增加职责,但具体职责在其子类中实现。抽象装饰类通过聚合关系定义一个抽象构件的对象,通过该对象可以调用装饰之前构件的方法,并通过其子类扩展该方法,达到装饰的目的;
|
||||||
|
- ConcreteDecorator(具体装饰类): 向构件增加新的功能。
|
||||||
|
|
||||||
|
以上面提到的手机为例,手机就是一个具体构件,而手机壳、手机贴纸和手机挂绳,都是具体的装饰类。以Jungle在冷天出门前精心打扮为例,Jungle本人是一个具体构件对象,各类衣裤围巾手套都是具体的装饰类对象。
|
||||||
|
|
||||||
|
## 3.装饰模式代码实例
|
||||||
|
本节以给手机带上手机壳、贴上手机贴纸、系上手机挂绳为例,展示装饰模式的代码。该例的UML图如下所示:
|
||||||
|
![avatar](https://github.com/FengJungle/DesignPattern/blob/master/10.DecoratorPattern/1.Picture/%E8%A3%85%E9%A5%B0%E6%A8%A1%E5%BC%8F%E5%AE%9E%E4%BE%8BUML%E5%9B%BE.png)
|
||||||
|
|
||||||
|
### 3.1.抽象构件类
|
||||||
|
```
|
||||||
|
// 抽象构件
|
||||||
|
class Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Component(){}
|
||||||
|
virtual ~Component(){}
|
||||||
|
virtual void operation() = 0;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
### 3.2.具体构件类
|
||||||
|
```
|
||||||
|
// 具体构件
|
||||||
|
class Phone :public Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Phone(){}
|
||||||
|
void operation(){
|
||||||
|
printf("手机\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
### 3.3.装饰类
|
||||||
|
#### 3.3.1.抽象装饰类
|
||||||
|
```
|
||||||
|
// 抽象装饰类
|
||||||
|
class Decorator :public Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Decorator(){}
|
||||||
|
Decorator(Component *c){
|
||||||
|
this->component = c;
|
||||||
|
}
|
||||||
|
void operation(){
|
||||||
|
this->component->operation();
|
||||||
|
}
|
||||||
|
Component *getComponent(){
|
||||||
|
return this->component;
|
||||||
|
}
|
||||||
|
void setComponent(Component *c){
|
||||||
|
this->component = c;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Component *component;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
抽象装饰类中有一个成员对象component,以及setter和getter方法。
|
||||||
|
#### 3.3.2.具体装饰类
|
||||||
|
具体装饰类一共有三个,分别是手机壳装饰类DecoratorShell、贴纸装饰类DecoratorSticker和挂绳装饰类DecoratorRope。每一个具体装饰类都增加了各自新的职责newBehavior。
|
||||||
|
```
|
||||||
|
// 具体装饰类:手机壳
|
||||||
|
class DecoratorShell:public Decorator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DecoratorShell(){}
|
||||||
|
DecoratorShell(Component *c){
|
||||||
|
this->setComponent(c);
|
||||||
|
}
|
||||||
|
void operation(){
|
||||||
|
this->getComponent()->operation();
|
||||||
|
this->newBehavior();
|
||||||
|
}
|
||||||
|
void newBehavior(){
|
||||||
|
printf("安装手机壳\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 具体装饰类:手机贴纸
|
||||||
|
class DecoratorSticker :public Decorator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DecoratorSticker(){}
|
||||||
|
DecoratorSticker(Component *c){
|
||||||
|
this->setComponent(c);
|
||||||
|
}
|
||||||
|
void operation(){
|
||||||
|
this->getComponent()->operation();
|
||||||
|
this->newBehavior();
|
||||||
|
}
|
||||||
|
void newBehavior(){
|
||||||
|
printf("贴卡通贴纸ֽ\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 具体装饰类:挂绳
|
||||||
|
class DecoratorRope :public Decorator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DecoratorRope(){}
|
||||||
|
DecoratorRope(Component *c){
|
||||||
|
this->setComponent(c);
|
||||||
|
}
|
||||||
|
void operation(){
|
||||||
|
this->getComponent()->operation();
|
||||||
|
this->newBehavior();
|
||||||
|
}
|
||||||
|
void newBehavior(){
|
||||||
|
printf("系手机挂绳\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
### 3.4.客户端代码示例
|
||||||
|
客户端展示了三段代码,分别为三个手机配上不同的装饰:
|
||||||
|
```
|
||||||
|
#include <iostream>
|
||||||
|
#include "DecoratorPattern.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
printf("\nJungle's first phone\n");
|
||||||
|
Component *c;
|
||||||
|
Component *com_Shell;
|
||||||
|
c = new Phone();
|
||||||
|
com_Shell = new DecoratorShell(c);
|
||||||
|
com_Shell->operation();
|
||||||
|
|
||||||
|
printf("\nJungle's second phone'\n");
|
||||||
|
Component *c2;
|
||||||
|
Component *com_Shell2;
|
||||||
|
c2 = new Phone();
|
||||||
|
com_Shell2 = new DecoratorShell(c2);
|
||||||
|
Component *com_Sticker;
|
||||||
|
com_Sticker = new DecoratorSticker(com_Shell2);
|
||||||
|
com_Sticker->operation();
|
||||||
|
|
||||||
|
printf("\nJungle's third phone'\n");
|
||||||
|
Component *c3;
|
||||||
|
Component *com_Shell3;
|
||||||
|
c3 = new Phone();
|
||||||
|
com_Shell3 = new DecoratorShell(c3);
|
||||||
|
Component *com_Sticker2;
|
||||||
|
com_Sticker2 = new DecoratorSticker(com_Shell3);
|
||||||
|
Component *com_Rope;
|
||||||
|
com_Rope = new DecoratorRope(com_Sticker2);
|
||||||
|
com_Rope->operation();
|
||||||
|
|
||||||
|
printf("\n\n");
|
||||||
|
|
||||||
|
delete c;
|
||||||
|
delete com_Shell;
|
||||||
|
delete c2;
|
||||||
|
delete com_Shell2;
|
||||||
|
delete com_Sticker;
|
||||||
|
delete c3;
|
||||||
|
delete com_Sticker2;
|
||||||
|
delete com_Rope;
|
||||||
|
|
||||||
|
system("pause");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## 3.5.效果
|
||||||
|
![avatar](https://github.com/FengJungle/DesignPattern/blob/master/10.DecoratorPattern/1.Picture/%E8%BF%90%E8%A1%8C%E5%9B%BE1.png)
|
||||||
|
|
||||||
|
由上述代码示例及运行结果可以看到,客户可以根据个人需要,动态增加构件的职责,但并不影响构件最基本的功能,不会修改原有的设计。同时,如果要增加新的职责(装饰),非常容易进行扩展。
|
||||||
|
|
||||||
|
## 4.总结
|
||||||
|
- 优点:
|
||||||
|
- 对于扩展一个类的新功能,装饰模式比继承更加灵活;
|
||||||
|
- 动态扩展一个对象的功能;
|
||||||
|
- 可以对一个对象进行多次装饰(如上述例子第二个手机和第三个手机);
|
||||||
|
- 具体构件类和具体装饰类可以独立变化和扩展,符合开闭原则。
|
||||||
|
- 缺点:
|
||||||
|
- 装饰模式中会增加很多小的对象,对象的区别主要在于各种装饰的连接方式不同,而并不是职责不同,大量小对象的产生会占用较多的系统资源;
|
||||||
|
- 装饰模式比继承模式更灵活,但也更容易出错,更难于排错。
|
||||||
|
- 适用场景:
|
||||||
|
- 在不影响其他对象的情况下,给单个对象动态扩展职责;
|
||||||
|
- 不适宜采用继承的方式进行扩展的时候,可以考虑使用装饰模式。
|
|
@ -0,0 +1,151 @@
|
||||||
|
# 如何吃到回锅肉?找厨师啊——外观模式实例解析
|
||||||
|
```
|
||||||
|
好不容易盼到周末啦!Jungle想吃点好的犒劳自己!吃什么呢?回锅肉!是的,吃回锅肉!
|
||||||
|
可是这过程好麻烦啊,先得去市场里买肉,买回来得洗好,然后切好,再炒肉,最后才能吃上!不仅过程繁杂,而且Jungle还得跟市场、厨房打交道,想想都头大。
|
||||||
|
如果有个厨师就好了,Jungle直接告诉厨师“我要吃回锅肉”,20分钟后厨师直接端上来就开吃。而中间那些买肉洗肉切肉的过程Jungle统统不关心了,而且Jungle也不必再关心市场和厨房,直接和厨师说句话就ok!真是方便!
|
||||||
|
```
|
||||||
|
![avatar](https://github.com/FengJungle/DesignPattern/blob/master/11.FacadePattern/1.Picture/%E5%A4%96%E8%A7%82%E6%A8%A1%E5%BC%8F%E4%B8%BE%E4%BE%8B%E5%9B%BE.png)
|
||||||
|
|
||||||
|
在这个例子中,厨师整合了一系列复杂的过程,外界(Jungle)只需与厨师交互即可。在软件设计模式中,有一类设计模式正式如此——外观模式。
|
||||||
|
|
||||||
|
## 1.外观模式简介
|
||||||
|
外观模式是一种使用频率较高的设计模式,它提供一个外观角色封装多个复杂的子系统,简化客户端与子系统之间的交互,方便客户端使用。外观模式可以降低系统的耦合度。如果没有外观类,不同的客户端在需要和多个不同的子系统交互,系统中将存在复杂的引用关系,如下图。引入了外观类,原有的复杂的引用关系都由外观类实现,不同的客户端只需要与外观类交互。
|
||||||
|
|
||||||
|
![avatar](https://github.com/FengJungle/DesignPattern/blob/master/11.FacadePattern/1.Picture/%E5%A4%96%E8%A7%82%E6%A8%A1%E5%BC%8F%E8%80%A6%E5%90%88%E5%BA%A6%E5%9B%BE.png)
|
||||||
|
|
||||||
|
```
|
||||||
|
外观模式:
|
||||||
|
为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
|
||||||
|
```
|
||||||
|
|
||||||
|
外观模式的应用很多,比如浏览器,用户要查找什么东西,不论是浏览知乎、腾讯或者CSDN,用户都只需要打开浏览器即可,剩下的搜索工作由浏览器完成。
|
||||||
|
|
||||||
|
## 2.外观模式结构
|
||||||
|
外观模式的UML结构图如下所示:
|
||||||
|
![avatar](https://github.com/FengJungle/DesignPattern/blob/master/11.FacadePattern/1.Picture/%E5%A4%96%E8%A7%82%E6%A8%A1%E5%BC%8F.png)
|
||||||
|
|
||||||
|
外观模式一共有以下角色:
|
||||||
|
|
||||||
|
- **Facade(外观角色)**:外观角色可以知道多个相关子系统的功能,它将所有从客户端发来的请求委派给相应的子系统,传递给相应的子系统处理。
|
||||||
|
- **SubSystem(子系统角色)**:子系统是一个类,或者由多个类组成的类的集合,它实现子系统具体的功能。
|
||||||
|
|
||||||
|
## 3.外观模式代码实例
|
||||||
|
电脑主机(Mainframe)中只需要按下主机的开机按钮(powerOn),即可调用其他硬件设备和软件的启动方法,如内存(Memory)的自检(selfCheck)、CPU的运行(run)、硬盘(HardDisk)的读取(read)、操作系统(OS)的载入(load)等。如果某一过程发生错误则电脑开机失败。
|
||||||
|
|
||||||
|
这里Jungle用外观模式来模拟该过程,该例子UML图如下:
|
||||||
|
![avatar](https://github.com/FengJungle/DesignPattern/blob/master/11.FacadePattern/1.Picture/%E5%A4%96%E8%A7%82%E6%A8%A1%E5%BC%8F%E5%AE%9E%E4%BE%8BUML%E5%9B%BE.png)
|
||||||
|
|
||||||
|
### 3.1.子系统类
|
||||||
|
本例中一共有4个子系统,因此设计4个类:Memory、CPU、HardDisk和OS,并且每个子系统都有自己独立的流程。
|
||||||
|
```
|
||||||
|
// subsystem: Memory
|
||||||
|
class Memory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Memory(){}
|
||||||
|
void selfCheck(){
|
||||||
|
printf("memory selfchecking......\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// subsystem: CPU
|
||||||
|
class CPU
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CPU(){}
|
||||||
|
void run(){
|
||||||
|
printf("running cpu......\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// subsystem: hardDisk
|
||||||
|
class HardDisk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HardDisk(){}
|
||||||
|
void read(){
|
||||||
|
printf("reading hardDisk......\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// subsystem: OS
|
||||||
|
class OS
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OS(){}
|
||||||
|
void load(){
|
||||||
|
printf("loading os.....\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
### 3.2.外观类设计
|
||||||
|
```
|
||||||
|
class Facade
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Facade(){
|
||||||
|
memory = new Memory();
|
||||||
|
cpu = new CPU();
|
||||||
|
hardDisk = new HardDisk();
|
||||||
|
os = new OS();
|
||||||
|
}
|
||||||
|
~Facade(){
|
||||||
|
delete memory;
|
||||||
|
delete cpu;
|
||||||
|
delete hardDisk;
|
||||||
|
delete os;
|
||||||
|
memory = nullptr;
|
||||||
|
cpu = nullptr;
|
||||||
|
hardDisk = nullptr;
|
||||||
|
os = nullptr;
|
||||||
|
}
|
||||||
|
Facade(const Facade& facade) = delete;
|
||||||
|
Facade operator=(const Facade& facade) = delete;
|
||||||
|
void powerOn(){
|
||||||
|
printf("power on……\n");
|
||||||
|
memory->selfCheck();
|
||||||
|
cpu->run();
|
||||||
|
hardDisk->read();
|
||||||
|
os->load();
|
||||||
|
printf("ready!\n");
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Memory *memory;
|
||||||
|
CPU *cpu;
|
||||||
|
HardDisk *hardDisk;
|
||||||
|
OS *os;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
### 3.3.客户端代码示例
|
||||||
|
```
|
||||||
|
#include <iostream>
|
||||||
|
#include "FacadePattern.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
Facade *facade = new Facade();
|
||||||
|
facade->powerOn();
|
||||||
|
|
||||||
|
printf("\n\n");
|
||||||
|
delete facade;
|
||||||
|
system("pause");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
看到了吗,客户端的代码就是如此简单,跟子系统无关!
|
||||||
|
|
||||||
|
### 3.4.效果
|
||||||
|
![avatar](https://github.com/FengJungle/DesignPattern/blob/master/11.FacadePattern/1.Picture/%E8%BF%90%E8%A1%8C%E5%9B%BE1.png)
|
||||||
|
|
||||||
|
## 4.总结
|
||||||
|
= 优点:
|
||||||
|
- 外观模式使得客户端不必关心子系统组件,减少了与客户端交互的对象的数量,简化了客户端的编程;
|
||||||
|
- 外观模式可以大大降低系统的耦合度;
|
||||||
|
- 子系统的变化并不需要修改客户端,只需要适当修改外观类即可;
|
||||||
|
- 子系统之间不会相互影响。
|
||||||
|
- 缺点:
|
||||||
|
- 如果需要增加或者减少子系统,需要修改外观类,违反开闭原则;
|
||||||
|
- 并不能限制客户端直接与子系统交互,但如果加强限制,又使得系统灵活度降低。
|
||||||
|
- 适用场景:
|
||||||
|
- 为访问一系列复杂的子系统提供一个统一的、简单的入口,可以使用外观模式;
|
||||||
|
- 客户端与多个子系统之间存在很大依赖,但在客户端编程,又会增加系统耦合度,且使客户端编程复杂,可以使用外观模式。
|
|
@ -0,0 +1,255 @@
|
||||||
|
# 细粒度对象的大面积复用~——实例分析享元模式
|
||||||
|
```
|
||||||
|
知道围棋吗?不会下围棋那总见过吧?四四方方的棋盘上,白色棋子和黑色棋子分布在棋盘的各个位置上。
|
||||||
|
棋子与棋子之间的区别是什么?除了颜色和位置,好像没什么不同了吧!也就是说,每个棋子对象的大部分状态都是一样的(形状、材料、质地等)。如果我们要设计一个程序来实现下围棋的功能,该如何来创建或者表示这上百个棋子对象呢?
|
||||||
|
```
|
||||||
|
|
||||||
|
类似的,你想输入一段英文段落,无论每个单词再长再复杂,也无非都是由26个字母中的几个组成的。上述两个示例的共同点在于,**整个环境中存在大量相同或者相似的、需要重复使用的对象**。针对这样的场景,面向对象设计中有一类值得借鉴的设计模式是不错的解决方案——享元模式
|
||||||
|
|
||||||
|
## 1.享元模式简介
|
||||||
|
如果一个系统在运行时创建太多相同或者相似的对象,会占用大量内存和资源,降低系统性能。**享元模式通过共享技术实现相同或相似的细粒度对象的复用,提供一个享元池存储已经创建好的对象,并通过享元工厂类将享元对象提供给客户端使用**。
|
||||||
|
```
|
||||||
|
享元模式:
|
||||||
|
运用共享技术有效地支持大量细粒度对象的复用。
|
||||||
|
```
|
||||||
|
**享元模式要求被共享的对象必须是细粒度对象**。如上面提到的输入英文段落的例子,26个字母可能随时被客户重复使用。尽管每个字母可能出现的位置不一样,但在物理上它们共享同一个对象(同一个实例)。利用享元模式,可以创建一个存储26个字母对象的享元池,需要时从享元池中取出。
|
||||||
|
|
||||||
|
![avatar](https://github.com/FengJungle/DesignPattern/blob/master/12.FlyweightPattern/1.Picture/%E4%BA%AB%E5%85%83%E6%A8%A1%E5%BC%8F%E4%B8%BE%E4%BE%8B%E5%9B%BE.png)
|
||||||
|
|
||||||
|
享元对象能够做到共享的关键在于区分了内部状态和外部状态:
|
||||||
|
|
||||||
|
- **内部状态**:存储在享元对象内部,不会随着环境的改变而改变的,内部状态可以共享。比如围棋中棋子的形状、大小,不会随着外部变化而变化;比如字母A,无论谁使用,都是A,不会变化;
|
||||||
|
- **外部状态**:随环境变化而变化、不可以共享的状态,如棋子的位置、颜色,如每个字母的位置。外部状态一般由客户端保存,在使用时再传入到享元对象内部。不同的外部状态之间是相互独立的,棋子A和棋子B的位置可以不同,并且不会相互影响。
|
||||||
|
|
||||||
|
## 2.享元模式结构
|
||||||
|
享元模式常常结合工厂模式一起使用,其结构包含抽象享元类、具体享元类、非共享具体享元类和享元工厂类:
|
||||||
|
|
||||||
|
- **Flyweight(抽象享元类)**:是一个抽象类,声明了具体享元类公共的方法,这些方法可以向外部提供享元对象的内部状态数据,也可以通过这些方法设置外部状态;
|
||||||
|
- **ConcreteFlyweight(具体享元类)**:具体实现抽象享元类声明的方法,具体享元类中为内部状态提供存储空间。具体享元类常常结合单例模式来设计实现,保证每个享元类对象只被创建一次,为每个具体享元类提供唯一的享元对象。
|
||||||
|
- **UnsharedConcreteFlyweight(非共享具体享元类)**:并不是所有抽象享元类的子类都需要被共享,可以将这些类设计为非共享具体享元类;
|
||||||
|
- **FlyweightFactory(享元工厂类)**:用于创建并管理享元对象,针对抽象享元类编程,将各种具体享元类对象存储在一个享元池中,享元池一般设计为一个存储键值对的集合(或者其他类型的集合),可结合工厂模式设计。客户需要某个享元对象时,如果享元池中已有该对象实例,则返回该实例,否则创建一个新的实例,给客户返回新的实例,并将新实例保存在享元池中。
|
||||||
|
|
||||||
|
![avatar](https://github.com/FengJungle/DesignPattern/blob/master/12.FlyweightPattern/1.Picture/%E4%BA%AB%E5%85%83%E6%A8%A1%E5%BC%8FUML%E5%9B%BE.png)
|
||||||
|
|
||||||
|
## 3.享元模式代码实例
|
||||||
|
很多网络设备都是支持共享的,如交换机(switch)、集线器(hub)等。多台中断计算机可以连接同一台网络设备,并通过网络设备进行数据转发。本节Jungle将使用享元模式来模拟共享网络设备的实例。
|
||||||
|
|
||||||
|
本例中,交换机(switch)和集线器(hub)是具体享元对象。UML图如下所示:
|
||||||
|
![avatar](https://github.com/FengJungle/DesignPattern/blob/master/12.FlyweightPattern/1.Picture/%E4%BA%AB%E5%85%83%E6%A8%A1%E5%BC%8F%E5%AE%9E%E4%BE%8BUML%E5%9B%BE.png)
|
||||||
|
|
||||||
|
### 3.1.抽象享元类
|
||||||
|
```
|
||||||
|
// 抽象享元类
|
||||||
|
class NetDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NetDevice(){}
|
||||||
|
virtual ~NetDevice(){}
|
||||||
|
virtual string getName() = 0;
|
||||||
|
|
||||||
|
/*void print(){
|
||||||
|
printf("NetDevice :%s\n",getName().c_str());
|
||||||
|
}*/
|
||||||
|
void print(int portNum){
|
||||||
|
printf("NetDevice :%s port: %d\n", getName().c_str(), portNum);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
### 3.2.具体享元类
|
||||||
|
具体享元类有集线器和交换机,实现了抽象享元类声明的方法。
|
||||||
|
```
|
||||||
|
// 具体享元类:集线器
|
||||||
|
class Hub :public NetDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Hub(){}
|
||||||
|
string getName(){
|
||||||
|
return "集线器";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 具体享元类:交换机
|
||||||
|
class Switch :public NetDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Switch(){}
|
||||||
|
string getName(){
|
||||||
|
return "交换机";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
### 3.3.享元工厂类
|
||||||
|
享元工厂类采用了单例模式,保证工厂实例的唯一性。采用一个vector作为共享池。
|
||||||
|
```
|
||||||
|
// 享元工厂类
|
||||||
|
class NetDeviceFactory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NetDevice* getNetDevice(char ch){
|
||||||
|
if (ch == 'S'){
|
||||||
|
return devicePool[1];
|
||||||
|
}
|
||||||
|
else if (ch == 'H'){
|
||||||
|
return devicePool[0];
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
printf("wrong input!\n");
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 单例模式:返回享元工厂类的唯一实例
|
||||||
|
static NetDeviceFactory* getFactory(){
|
||||||
|
if (instance == NULL){
|
||||||
|
m_mutex.lock();
|
||||||
|
if (instance == NULL){
|
||||||
|
instance = new NetDeviceFactory();
|
||||||
|
}
|
||||||
|
m_mutex.unlock();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
NetDeviceFactory(){
|
||||||
|
Hub *hub = new Hub();
|
||||||
|
Switch *switcher = new Switch();
|
||||||
|
devicePool.push_back(hub);
|
||||||
|
devicePool.push_back(switcher);
|
||||||
|
}
|
||||||
|
static NetDeviceFactory* instance;
|
||||||
|
static std::mutex m_mutex;
|
||||||
|
|
||||||
|
// 共享池:用一个vector来表示
|
||||||
|
vector<NetDevice*> devicePool;
|
||||||
|
};
|
||||||
|
|
||||||
|
NetDeviceFactory* NetDeviceFactory::instance = NULL;
|
||||||
|
std::mutex NetDeviceFactory::m_mutex;
|
||||||
|
```
|
||||||
|
## 3.4.客户端代码示例
|
||||||
|
```
|
||||||
|
#include <iostream>
|
||||||
|
#include "FlyweightPattern.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
NetDeviceFactory *factory = NetDeviceFactory::getFactory();
|
||||||
|
|
||||||
|
NetDevice *device1, *device2, *device3, *device4;
|
||||||
|
|
||||||
|
// 客户端1获取一个hub
|
||||||
|
device1 = factory->getNetDevice('H');
|
||||||
|
device1->print(1);
|
||||||
|
// 客户端2获取一个hub
|
||||||
|
device2 = factory->getNetDevice('H');
|
||||||
|
device2->print(2);
|
||||||
|
// 判断两个hub是否是同一个
|
||||||
|
printf("判断两个hub是否是同一个:\n");
|
||||||
|
printf("device1:%p\ndevice2:%p\n", device1, device2);
|
||||||
|
|
||||||
|
printf("\n\n\n\n");
|
||||||
|
// 客户端3获取一个switch
|
||||||
|
device3 = factory->getNetDevice('S');
|
||||||
|
device3->print(1);
|
||||||
|
// 客户端4获取一个switch
|
||||||
|
device4 = factory->getNetDevice('S');
|
||||||
|
device4->print(2);
|
||||||
|
// 判断两个switch是否是同一个
|
||||||
|
printf("判断两个switch是否是同一个:\n");
|
||||||
|
printf("device3:%p\ndevice4:%p\n", device3, device4);
|
||||||
|
|
||||||
|
printf("\n\n");
|
||||||
|
|
||||||
|
delete factory;
|
||||||
|
delete device1;
|
||||||
|
delete device2;
|
||||||
|
delete device3;
|
||||||
|
delete device4;
|
||||||
|
factory = nullptr;
|
||||||
|
device1 = nullptr;
|
||||||
|
device2 = nullptr;
|
||||||
|
device3 = nullptr;
|
||||||
|
device4 = nullptr;
|
||||||
|
|
||||||
|
system("pause");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
客户端代码中,两个客户端分别获取集线器,Jungle打印出两个集线器的地址,来判断是否是同一个对象。同理,对交换机,Jungle也进行类似的判断。
|
||||||
|
### 3.5.效果
|
||||||
|
|
||||||
|
![avatar](https://github.com/FengJungle/DesignPattern/blob/master/12.FlyweightPattern/1.Picture/%E8%BF%90%E8%A1%8C%E5%9B%BE1.png)
|
||||||
|
|
||||||
|
由测试结果可以看出,两个集线器对象的地址是相同的,说明它们都是同一个实例对象,两个交换机也都指向同一个交换机实例对象。由此说明本例的代码实现了网络设备的共享。
|
||||||
|
|
||||||
|
### 3.6.有外部状态的享元模式
|
||||||
|
进一步,尽管不同的终端计算机可能会共享同一个集线器(交换机),但是每个计算机接入的端口(port)是不一样的,**端口就是每个享元对象的外部状态。 在享元模式的使用过程中,内部状态可以作为具体享元类的成员对象,而外部状态可以通过外部注入的方式添加到具体享元类中**。
|
||||||
|
|
||||||
|
“通过外部注入”,因此,客户端可以通过函数传参的方式将“端口”号注入具体享元类:
|
||||||
|
```
|
||||||
|
// 抽象享元类
|
||||||
|
class NetDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NetDevice(){}
|
||||||
|
virtual string getName() = 0;
|
||||||
|
|
||||||
|
/*void print(){
|
||||||
|
printf("NetDevice :%s\n",getName().c_str());
|
||||||
|
}*/
|
||||||
|
void print(int portNum){
|
||||||
|
printf("NetDevice :%s port: %d\n", getName().c_str(), portNum);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
那么客户端的使用方式将变为:
|
||||||
|
```
|
||||||
|
#include <iostream>
|
||||||
|
#include "FlyweightPattern.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
NetDeviceFactory *factory = NetDeviceFactory::getFactory();
|
||||||
|
|
||||||
|
NetDevice *device1, *device2, *device3, *device4;
|
||||||
|
|
||||||
|
// 客户端2获取一个hub
|
||||||
|
device1 = factory->getNetDevice('H');
|
||||||
|
device1->print(1);
|
||||||
|
// 客户端2获取一个hub
|
||||||
|
device2 = factory->getNetDevice('H');
|
||||||
|
device2->print(2);
|
||||||
|
// 判断两个hub是否是同一个
|
||||||
|
printf("判断两个hub是否是同一个:\n");
|
||||||
|
printf("device1:%p\ndevice2:%p\n", device1, device2);
|
||||||
|
|
||||||
|
printf("\n\n\n\n");
|
||||||
|
// 客户端3获取一个switch
|
||||||
|
device3 = factory->getNetDevice('S');
|
||||||
|
device3->print(1);
|
||||||
|
// 客户端4获取一个hub
|
||||||
|
device4 = factory->getNetDevice('S');
|
||||||
|
device4->print(2);
|
||||||
|
// 判断两个hub是否是同一个
|
||||||
|
printf("判断两个switch是否是同一个:\n");
|
||||||
|
printf("device3:%p\ndevice4:%p\n", device3, device4);
|
||||||
|
|
||||||
|
printf("\n\n");
|
||||||
|
|
||||||
|
system("pause");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
效果如下:
|
||||||
|
![avatar](https://github.com/FengJungle/DesignPattern/blob/master/12.FlyweightPattern/1.Picture/%E8%BF%90%E8%A1%8C%E5%9B%BE2.png)
|
||||||
|
|
||||||
|
## 4.总结
|
||||||
|
- 优点:
|
||||||
|
- 享元模式通过享元池存储已经创建好的享元对象,实现相同或相似的细粒度对象的复用,大大减少了系统中的对象数量,节约了内存空间,提升了系统性能;
|
||||||
|
- 享元模式通过内部状态和外部状态的区分,外部状态相互独立,客户端可以根据需求任意使用。
|
||||||
|
- 缺点:
|
||||||
|
- 享元模式需要增加逻辑来取分出内部状态和外部状态,增加了编程的复杂度;
|
||||||
|
- 适用环境:
|
||||||
|
- 当一个系统中有大量重复使用的相同或相似对象时,使用享元模式可以节约系统资源;
|
||||||
|
- 对象的大部分状态都可以外部化,可以将这些状态传入对象中。
|
Loading…
Reference in New Issue