目录
一、装饰器模式
模式思想
模式简介
模式优点
模式缺点
代码实现
情景模拟
代码实现
运行结果
二、适配器模式
模式简介
介绍
优点
缺点
代码实现
情景模拟
一、装饰器模式
模式思想
模式简介
装饰器模式(Decorator Pattern
)允许向一个现有的对象添加新的功能,同时又不改变其结构。
这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
意图:
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
何时使用:
在不想增加很多子类的情况下扩展类。如何解决:
将具体功能职责划分,同时继承装饰器模式
模式优点
装饰类
和被装饰类
可以独立发展,不会相互耦合,装饰器模式是继承的一个替代模式,装饰器模式可以动态地扩展实现一个类的功能。
模式缺点
多层装饰比较复杂。
代码实现
情景模拟
我们还是拿汽车
来举例子吧。
现在的汽车越来越智能化了,什么定速巡航
、自动刹车
、车道偏离
等功能都逐渐进入我们的生活,为我们带来了更为便利的出行。
假设上述提到的定速巡航
、自动刹车
、车道偏离
三个功能就是我们想要为汽车装饰的功能,汽车默认的功能配置有:基本配置
。
得到具体的汽车(宝马
、奥迪
、奔驰
)后,我们可以选择性地增加装饰功能。
代码实现
具体实现如下:
//抽象基类
class Car
{
public:virtual void show() = 0;
};//三个实体的基类
class Bmw : public Car
{
public:void show(){cout << "这是一辆宝马汽车,配置有:基本配置";}
};
class Audi : public Car
{
public:void show(){cout << "这是一辆奥迪汽车,配置有:基本配置";}
};
class Benz : public Car
{
public:void show(){cout << "这是一辆奔驰汽车,配置有:基本配置";}
};//装饰器的基类
class CarDecorator : public Car
{
public:CarDecorator(Car* p):pCar(p){}
private:Car* pCar;
};//装饰器1 :定速巡航
class ConcreateDecorator1 : public Car
{
public:ConcreateDecorator1(Car* p):pCar(p){}void show(){pCar->show();cout << "定速巡航功能,";}
private:Car* pCar;
};
//装饰器2 :自动刹车
class ConcreateDecorator2 : public Car
{
public:ConcreateDecorator2(Car* p) :pCar(p){}void show(){pCar->show();cout << "自动刹车";}
private:Car* pCar;
};
//装饰器3 :车道偏离
class ConcreateDecorator3 : public Car
{
public:ConcreateDecorator3(Car* p) :pCar(p){}void show(){pCar->show();cout << "车道偏离";}
private:Car* pCar;
};
运行结果
接着我们可以使用如下代码:
void main()
{Car* p1 = new ConcreateDecorator1(new Bmw());p1 = new ConcreateDecorator2(p1);p1->show();cout << endl;Car* p2 = new ConcreateDecorator2(new Audi());p2->show();cout << endl;Car* p3 = new ConcreateDecorator3(new Benz());p3->show();cout << endl;
}
可以看到,我们在运行代码中:
给宝马汽车
增加了Decorator01(定速巡航)
、Decorator02(自动刹车)
、Decorator03(车道偏离)
的配置;
给奥迪汽车
增加了Decorator02(自动刹车)
配置;
给奔驰汽车
增加了Decorator03(车道偏离)
配置。
二、适配器模式
模式简介
介绍
适配器模式(Adapter Pattern
)是作为两个不兼容的接口之间的桥梁。
这种类型的设计模式属于结构型模式。
意图:
将一个类的接口转换成为客户希望的另一个接口。
适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。主要解决:
主要解决在软件系统中,常常要将一些“现存的对象”放到新环境中,而新环境的接口是现有对象不能满足的。
优点
- 可以让任何两个没有关联的类一起工作;
- 提高了类的复用;
- 增加了类的透明度;
- 灵活性好。
缺点
过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
代码实现
情景模拟
同样的,我们来模拟这么一个场景:
我们要安装一个投影仪:能够将电脑上的画面投影出来,对于投影仪来说,常见的接口有:VGA、HTMI接口,但是对于不同的电脑,接口一定是固定的,一个电脑必然只有一种接口,我们往往会出现接口不适配的情况。
我们就来模拟不兼容的两个接口:VGA
和HDMI
之间,如何通过一个适配器adapter
连接在一起工作的。
当我们的电脑是VGA接口的时候,我们很自然能连接VGA的投影仪,但是当我们想要去连接HTMI的投影仪时,问题必然就出现了。
我们可以添加一个适配器类:
/*
适配器模式:让不兼容的接口可以在一起工作VGA接口的电脑,投影仪也是VGA接口
*///VGA接口类
class VGA
{
public:virtual void play() = 0;
};//表示支持VGA接口的投影仪
class TV1 :public VGA
{
public:void play(){cout << "通过VGA接口连接投影仪,进行视频播放 " << endl;}
};//实现一个电脑类(只支持VGA接口)
class Computer
{
public://由于电脑支持VGA接口,所以该方法的参数智能支持VGA即可的指针/引用void playVideo(VGA* pVGA){pVGA->play();}
};
/*
方法1:换一个支持HDMI接口的电脑,这个就叫代码重构
方法2:买一个转换头(适配器),能够把VGA信号转换成?HDMI信号,这个叫添加适配器类*///进了一批新的投影仪,但是新的投影仪都是只支持HDMI接口
//HDMI接口类
class HDMI
{
public:virtual void play() = 0;
};
//表示支持HDMI接口的投影仪
class TV2 :public HDMI
{
public:void play(){cout << "通过HTMI接口连接投影仪,进行视频播放 " << endl;}
};//由于电脑(VGA接口)和投影仪(HTMI接口)无法直接相连,所以需要添加适配器类
class VGATOHDMI :public VGA
{
public:VGATOHDMI(HDMI* p) :pHdmi(p){}void play()//该方法相当于就是转换头,做不同接口的信号转换{pHdmi->play();}
private:HDMI* pHdmi;
};
运行一下:
void main()
{Computer cc;cc.playVideo(new TV1());cc.playVideo(new VGATOHDMI(new TV2()));}
结果: