1.代理模式
2.装饰模式
3.外观模式
4.适配器模式
1.代理模式
让类和类进行组合,获得更大的结构
代理模式(Proxy Pattern)是一个使用率非常高的模式,其定义如下:
Provide a surrogate or placeholder for another object to control access to it(为其他对象提供一种代理以控制对这个对象的访问)
1.1模式中的角色和职责
1.pngsubject(抽象主题角色):真实主题与代理主题的共同接口。
RealSubject(真实主题角色):定义了代理角色所代表的真实对象。
Proxy(代理主题角色):含有对真实主题角色的引用,代理角色通常在将客户端调用传递给真是主题对象之前或者之后执行某些操作,而不是单纯返回真实的对象。
1.2代理模式的案例
2.png#include <iostream>
#include <string>
using namespace std;
//商品
class Item
{
public:
Item(string kind,bool fact){
this->kind = kind;
this->fact = fact;
}
string getString(){
return this->kind;
}
bool getFact(){
return this->fact;
}
private:
string kind;//商品种类
bool fact;//商品真假
};
//抽象的购物方式
class Shopping
{
public:
virtual void buy(Item *it) = 0;//抽象的买东西方法
};
//韩国购物
class KoreaShopping:public Shopping
{
public:
virtual void buy(Item *it){
cout<<"去韩国买了"<<it->getKind()<<endl;
}
}
//美国购物
class USAShopping:public Shopping
{
public:
virtual void buy(Item *it){
cout<<"去美国买了"<<it->getKind()<<endl;
}
}
//发现 123步都一样的,能不能找个代理,直接把这3步都给它完成
//海外代理
class OverseasProxy:public Shopping
{
public:
OverseasProxy(Shopping *shopping){//你传进来是美国shopping还是韩国shopping我都有据可依
this->shopping = shopping;
}
virtual void buy(Item *it){
//1 辨别商品的真假
//2 进行购买
//3 通过海关安检 带回祖国
shopping->buy(it);
//...
}
private:
Shopping *shopping;//有一个购物方式
}
int main(void)
{
//1 辨别商品的真假
//2 进行购买
//3 通过海关安检 带回祖国
Item it1("nike鞋",true);
Item it2("CET-4证书",false);
#if 0
//想去韩国买一个鞋
Shopping *koreaShopping = new KoreaShopping;
//1辨别商品真伪
if (it1.getFact() == true) {
//2去韩国买了这个商品
koreaShopping->buy(&it1);
//3安检
cout<<"通过海关安检,带回祖国"<<endl;
}
else{
cout<<"发现假货,不会购买"<<endl;
}
#endif
Shopping *usaShopping = new USAShopping;
Shopping *overseaProxy = new OverseasProxy(usaShopping);
overseaProxy->buy(&it2);
return 0;
}
案例2
#include <iostream>
#include <string>
using namespace std;
//抽象的美女类
class BeautyGirl
{
public:
// 1 跟男人抛媚眼
virtual void MakeEyesWithMan() = 0;
// 2 与男人共度美好的约会
virtual void HappyWithMan() = 0;
};
//潘金莲
class JinLian:public BeautyGirl
{
public:
//1 跟男人抛媚眼
virtual void MakeEyesWithMan(){
cout<<"潘金莲抛了一个媚眼"<<endl;
}
//2 与男人共度美好的约会
virtual void HappyWithMan(){
cout<<"潘金莲跟你共度约会"<<endl;
}
}
//王婆 代理
class WangPo:public BeautyGirl
{
public:
WangPo(BeautyGirl *girl){
this->girl = girl;
}
//1 跟男人抛媚眼
virtual void MakeEyesWithMan(){
girl->MakeEyesWithMan();
}
//2 与男人共度美好的约会
virtual void HappyWithMan(){
girl->HappyWithMan();
}
private:
BeautyGirl *girl;
}
//西门大官人
int main(void)
{
BeautyGirl *jinlian = new JinLian;
WangPo *wangpo = new WangPo(jinlian);
wangpo->MakeEyesWithMan();
wangpo->HappyWithMan();
return 0;
}
1.3代理模式的优点
- 职责清晰:真是的角色就是实现实际的业务逻辑,不用关心其他非本职责的业务,通过后期的代理完成一件事务,附带的效果就是编程简洁清晰。
- 高扩展性:具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃出如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用
- 智能化:这在我们以上的讲解中还没有体现出来,不过我们可以在动态代理中再加深学习
1.4代理模式的使用场景
为什么要使用代理?想想现实世界,打官司为什么要找个律师?因为你不想参与到中间过程的是是非非,只要完成自己的答辩就成,其他的事比如事前调查、事后追查都有律师来搞定,这就是为了减轻你的负担。
归根到底,就是一劳永逸的编程体验,特别是自己写库给别人使用,封装的加强也使得库的使用者简单上手!
2.装饰模式
装饰模式(Decorator Pattern):动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。
2.1装饰模式中的角色和职责
装饰模式通用类图Component(抽象构件):它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
ConcreteComponent(具体构件):它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。
Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。
2.2装饰器案例
案例类图#include <iostream>
#include <string>
using namespace std;
//抽象的手机类
class Phone
{
public:
virtual void show() = 0;
};
class iPhone:public Phone
{
public:
virtual void show(){
cout<<"秀出了iphone"<<endl;
}
};
class Mi:public Phone
{
public:
virtual void show(){
cout<<"秀出了小米"<<endl;
}
};
//写一个抽象装饰器
class Decorator:public Phone
{
public:
Decorator(Phone *phone){
this->phone = phone;
}
virtual void show(){
this->phone->show();
}
protected:
Phone *phone;//拥有一个所有手机的父类指针
}
//具体的手机贴膜装饰器
class MoDecorator:public Decorator
{
public:
MoDecorator(Phone *phone):Decorator(phone){};
virtual void show(){
this->phone->show();//保持原有的show方法
this->mo();
}
//膜装饰器,可以修饰的方法
void mo(){
cout<<"手机有了贴膜"<<endl;
}
}
//皮套的装饰器类
class TaoDecorator:public Decorator
{
public:
TaoDecorator(Phone *phone):Decorator(phone){};
virtual void show(){
this->phone->show();
this->tao();
}
void tao(){
cout<<"手机有了皮套"<<endl;
}
}
int main(void)
{
Phone *phone = new iPhone;
phone->show();
cout<<"------------------------"<<endl;
Phone *moPhone = new MoDecorator(phone);
moPhone->show();
cout<<"------------------------"<<endl;
Phone *taoPhone = new TaoDecorator(phone);
taoPhone->show();
cout<<"------------------------"<<endl;
Phone *moTaoPhone = new TaoDecorator(moPhone);
moTaoPhone->show();//moPhone.show() + tao() == phone.show() + mo() + tao()
delete phone;
delete moPhone;
delete taoPhone;
delete moTaoPhone;
return 0;
}
2.3装饰模式的优缺点
优点:
- 对于扩展一个对象的功能,装饰模式比继承更加灵活,不会导致类的个数急剧增加。
- 可以通过一种动态地方式来扩展一个对象的功能,从而实现不同的行为。
- 可以对一个对象进行多次修饰
- 具体构建类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构建类和具体装饰类,原有类库代码无需改变,符合“开闭原则”
缺点:
- 使用装饰模式进行系统设计时将产生很多小对象,大量小对象的产生势必会占用更多的系统资源,影响程序的性能
2.4适用场景
- 动态、透明的方式给单个对象添加职责。
- 当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式。
装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话 说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。
2.5例子
#include <iostream>
#include <string>
using namespace std;
class Hero
{
public:
virtual void status() = 0;
};
class Akali:public Hero
{
public:
virtual void status(){
cout<<"HP:1000"<<endl;
cout<<"AP:500"<<endl;
cout<<"AD:50"<<endl;
}
};
//英雄的装饰器
class Decorator:public Hero
{
public:
Decorator(Hero* hero){
this->hero = hero;
}
virtual void status(){
this->hero->status();
}
protected:
Hero *hero;
};
//日炎斗篷装饰器
class RYDecorator:public Decorator
{
public:
RYDecorator(Hero *hero):Decorator(hero){}
virtual void status(){
this->hero->status();//先调用被装饰的 英雄的基本状态
cout<<"HP + 100000"<<endl;
}
};
//
class SYQZDecorator:public Decorator
{
public:
SYQZDecorator(Hero *hero):Decorator(hero){}
virtual void status(){
this->hero->status();
cout<<"AP+5000"<<endl;
}
};
int main(void)
{
Hero *akali = new Akali;
cout<<"akali的初始状态"<<endl;
akali->status;
cout<<"通过日炎斗篷创建新的akali"<<endl;
Hero *ryAkali = new RYDecorator(akali);
ryAkali->status();
cout<<"再通过深渊权杖装备装饰日炎akali"<<endl;
Hero *syqzAkali = new SYQZDecorator(ryAkali);
syqzAkali->status();
return 0;
}
3.外观模式
根据迪米特法则,如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。
Facade模式也叫外观模式,是由GoF提出的23种设计模式中的一种。Facade模式为一组具有类似功能的类群,比如类库,子系统等等,提供一个一致的简单的界面。这个一致的简单的界面被称作facade。
3.1外观模式中角色和职责
外观模式类图Façade(外观角色):为调用方,定义简单的调用接口。
SubSystem(子系统角色):功能提供者。指提供功能的类群(模块或子系统)。
3.2外观模式案例
练习:
根据类图,实现家庭影院外观模式应用。
实现KTV模式:电视打开,灯关掉,音响打开,麦克风打开,dvd打开;
实现游戏模式:电视打开,音响打开,游戏机打开。
#include <iostream>
#include <string>
using namespace std;
/*
实现KTV模式:电视打开,灯关掉,音响打开,麦克风打开,dvd打开;
实现游戏模式:电视打开,音响打开,游戏机打开。
*/
class TV
{
public:
void On(){
cout<<"电视打开了"<<endl;
}
void Off(){
cout<<"电视关闭了"<<endl;
}
};
class DVD
{
public:
void On(){
cout<<"DVD打开了"<<endl;
}
void Off(){
cout<<"DVD关闭了"<<endl;
}
};
class XBox
{
public:
void On(){
cout<<"XBox打开了"<<endl;
}
void Off(){
cout<<"XBox关闭了"<<endl;
}
};
class MikePhone
{
public:
void On(){
cout<<"MikePhone打开了"<<endl;
}
void Off(){
cout<<"MikePhone关闭了"<<endl;
}
};
class Light
{
public:
void On(){
cout<<"Light打开了"<<endl;
}
void Off(){
cout<<"Light关闭了"<<endl;
}
};
class HomePlayer
{
public:
//ktv模式的接口
void doKTV(){
light.off();
tv.On();
dvd.On();
}
//游戏模式的接口
void doGame(){
tv.On();
xbox.On();
}
Light light;
TV tv;
MikePhone mkp;
XBox xbox;
DVD dvd;
}
int main(void)
{
HomePlayer hp;
cout<<"进入ktv模式"<<endl;
hp.doKTV();
cout<<"进入游戏模式"<<endl;
hp.doGame();
return 0;
}
3.3外观模式的优缺点
优点:
(1) 它对客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目,并使得子系统使用起来更加容易。通过引入外观模式,客户端代码将变得很简单,与之关联的对象也很少。
(2) 它实现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端,只需要调整外观类即可。
(3)** 一个子系统的修改对其他子系统没有任何影响。**
缺点:
(1) 不能很好地限制客户端直接使用子系统类,如果对客户端访问子系统类做太多的限制则减少了可变性和灵活性。
(2) 如果设计不当,增加新的子系统可能需要修改外观类的源代码,违背了开闭原则。
3.4适用场景
(1) 复杂系统需要简单入口使用。
(2) 客户端程序与多个子系统之间存在很大的依赖性。
(3) 在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。
4.适配器模式
适配器模式将一个类的接口转换成客户希望的另外一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
4.1适配器模式中的角色和职责
适配器模式类图- Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
- Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
- Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
根据对象适配器模式结构图,在对象适配器中,客户端需要调用request()方法,而适配者类Adaptee没有该方法,但是它所提供的specificRequest()方法却是客户端所需要的。为了使客户端能够使用适配者类,需要提供一个包装类Adapter,即适配器类。这个包装类包装了一个适配者的实例,从而将客户端与适配者衔接起来,在适配器的request()方法中调用适配者的specificRequest()方法。因为适配器类与适配者类是关联关系(也可称之为委派关系),所以这种适配器模式称为对象适配器模式。
4.2适配器模式的案例
案例类图#include <iostream>
#include <string>
using namespace std;
//5v电压类
#if 0
class V5{
public:
void useV5(){
cout<<"使用了5v电压"<<endl;
}
};
#endif
class V5{
public:
virtual void useV5() = 0;
};
//目前只有220V电压的类,没有5V的类
class V220{
public:
void useV220(){
cout<<"使用了220v电压"<<endl;
}
};
//定义一个中间的适配器
class Adapter:public V5
{
public:
Adapter(V220 *v220){
this->v220 = v220;
}
virtual void useV5(){
v220->useV220();//调用需要另外的方法
}
~Adapter(){
if(this->v220 != NULL){
delete this->v220;
this->v220 = NULL;
}
}
private:
V220 *v220;
};
class iPhone
{
public:
iPhone(V5 *v5){
this->v5 = v5;
}
//充电的方法
void charge(){
cout<<"iphone手机进行了充电"<<endl;
v5->useV5;
}
~iPhone(){
if(this->v5 != NULL){
delete this->v5;
}
}
private:
V5 *v5;
}
int main(void)
{
#if 0
V5 *v5 = new V5;
iPhone *phone = new iPhone(v5);
delete phone;
#endif
iPhone *phone = new iPhone(new Adapter(new V220));
phone->charge();
return 0;
}
4.3适配器模式优缺点
优点:
(1) 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
(2) 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。
(3) 灵活性和扩展性都非常好,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。
缺点:
适配器中置换适配者类的某些方法比较麻烦。
4.4适用场景
(1) 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
(2) 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
网友评论