设计模式4 结构型模式


目录代理模式装饰器外观模式适配器模式


代理模式,美国,韩国代理购物

chunli@linux:~$catmain.cpp#include<iostream>usingnamespacestd;classItem//商品{public:Item(stringkind,boolfact){this->kind=kind;this->fact=fact;}stringgetKind(){returnkind;}boolgetFact(){returnfact;}private:stringkind;boolfact;};classShopping//抽象的购物方式{public:virtualvoidbuy(Item*it)=0;};classKoreaShopping:publicShopping//Korea购物{public:virtualvoidbuy(Item*it){cout<<"去Korea买了"<<it->getKind()<<endl;}};classUSAShopping:publicShopping//USA购物{public:virtualvoidbuy(Item*it){cout<<"去USA买了"<<it->getKind()<<endl;}};intmain(){Itemit_1("Nike鞋",true);if(it_1.getFact()==true)//辨别产品真假{cout<<"发现真货"<<endl;Shopping*koreaShopping=newKoreaShopping;koreaShopping->buy(&it_1);//代理cout<<"过安检"<<endl;}else{cout<<"假货,不要买"<<endl;}Itemit_2("英语证书",false);return0;}chunli@linux:~$g++main.cpp-Wall&&./a.out发现真货去Korea买了Nike鞋过安检chunli@linux:~$


代理模式,海外代理韩国与美国

chunli@linux:~$catmain.cpp#include<iostream>usingnamespacestd;classItem//商品{public:Item(stringkind,boolfact){this->kind=kind;this->fact=fact;}stringgetKind(){returnkind;}boolgetFact(){returnfact;}private:stringkind;boolfact;};classShopping//抽象的购物方式{public:virtualvoidbuy(Item*it)=0;};classKoreaShopping:publicShopping//Korea购物{public:virtualvoidbuy(Item*it){cout<<"去Korea买了"<<it->getKind()<<endl;}};classUSAShopping:publicShopping//USA购物{public:virtualvoidbuy(Item*it){cout<<"去USA买了"<<it->getKind()<<endl;}};classOverseaProxy:publicShopping{public:OverseaProxy(Shopping*shopping){this->shopping=shopping;}virtualvoidbuy(Item*it){//购买之前............if(it->getFact()==false){cout<<"发现假货,不要购买"<<endl;return;}//开始购买shopping->buy(it);//购买之后cout<<"通过安检,后买成功!"<<endl;}private:Shopping*shopping;};intmain(){Shopping*usaShopping=newUSAShopping;Shopping*overseaProxy=newOverseaProxy(usaShopping);Itemit1("英语证书",false);overseaProxy->buy(&it1);Itemit2("Dell服务器",true);overseaProxy->buy(&it2);return0;}chunli@linux:~$g++main.cpp-Wall&&./a.out发现假货,不要购买去USA买了Dell服务器通过安检,后买成功!chunli@linux:~$

看图:

subject(抽象主题角色):真实主题与代理主题的共同接口。

RealSubject(真实主题角色):定义了代理角色所代表的真实对象。

Proxy(代理主题角色): 含有对真实主题角色的引用,代理角色通常在

将客户端调用传递给真是主题对象之前或者之后执行某些操作,而不是单纯返

回真实的对象。


优点:

(1) 能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。

(2) 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源

代码,符合开闭原则,系统具有较好的灵活性和可扩展性。

缺点:

(1) 代理实现较为复杂。


---------------------------------------------------------------------




装饰器:把手机裸机 装饰成有贴膜的手机

chunli@linux:~$catmain.cpp#include<iostream>usingnamespacestd;classPhone{public:virtualvoidshow()=0;};classiPhone:publicPhone{public:virtualvoidshow(){cout<<"秀出了iPhone"<<endl;}};classMi:publicPhone{public:virtualvoidshow(){cout<<"秀出了小米"<<endl;}};//抽象装饰器classDecorator:publicPhone{public:Decorator(Phone*phone){this->phone=phone;}virtualvoidshow()=0;protected:Phone*phone;//拥有一个手机的父类指针};//贴膜装饰器classMoDecorator:publicDecorator{public:MoDecorator(Phone*phone):Decorator(phone){}virtualvoidshow(){this->phone->show();this->mo();}voidmo(){cout<<"手机贴膜了"<<endl;}};intmain(){Phone*phone=newiPhone;//创建一个裸机phone->show();//裸机show()cout<<"---------------"<<endl;Phone*mophone=newMoDecorator(phone);mophone->show();return0;}chunli@linux:~$g++main.cpp-Wall&&./a.out秀出了iPhone---------------秀出了iPhone手机贴膜了chunli@linux:~$


装饰器,在贴膜的手机,加壳

chunli@linux:~$g++main.cpp-Wall&&./a.out秀出了iPhone---------------秀出了iPhone手机贴膜了---------------秀出了iPhone手机贴膜了手机加壳了chunli@linux:~$chunli@linux:~$catmain.cpp#include<iostream>usingnamespacestd;classPhone{public:virtualvoidshow()=0;};classiPhone:publicPhone{public:virtualvoidshow(){cout<<"秀出了iPhone"<<endl;}};classMi:publicPhone{public:virtualvoidshow(){cout<<"秀出了小米"<<endl;}};//抽象装饰器classDecorator:publicPhone{public:Decorator(Phone*phone){this->phone=phone;}virtualvoidshow()=0;protected:Phone*phone;//拥有一个手机的父类指针};//贴膜装饰器classMoDecorator:publicDecorator{public:MoDecorator(Phone*phone):Decorator(phone){}virtualvoidshow(){this->phone->show();this->mo();}voidmo(){cout<<"手机贴膜了"<<endl;}};//手机壳装饰器classShellDecorator:publicDecorator{public:ShellDecorator(Phone*phone):Decorator(phone){}virtualvoidshow(){this->phone->show();this->shell();}voidshell(){cout<<"手机加壳了"<<endl;}};intmain(){Phone*phone=newiPhone;//创建一个裸机phone->show();//裸机show()cout<<"---------------"<<endl;Phone*mophone=newMoDecorator(phone);mophone->show();cout<<"---------------"<<endl;Phone*shellPhone=newShellDecorator(mophone);shellPhone->show();return0;}chunli@linux:~$g++main.cpp-Wall&&./a.out秀出了iPhone---------------秀出了iPhone手机贴膜了---------------秀出了iPhone手机贴膜了手机加壳了chunli@linux:~$



看图


Component(抽象构件): 它是具体构件和抽象装饰类的共同父类,声

明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理

未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。

ConcreteComponent(具体构件): 它是抽象构件类的子类,用于定

义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额

外的职责(方法)。

Decorator(抽象装饰类): 它也是抽象构件类的子类,用于给具体构件

增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引

用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,

以达到装饰的目的。

ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,负责向

构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在

抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。




4.2.3 装饰模式的优缺点

优点:

(1) 对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数

急剧增加。

(2) 可以通过一种动态的方式来扩展一个对象的功能,从而实现不同的行为。

(3) 可以对一个对象进行多次装饰。

(4) 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构

件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”。

缺点:

(1) 使用装饰模式进行系统设计时将产生很多小对象,大量小对象的产生势必会

占用更多的系统资源,影响程序的性能。



外观模式: 演示:为了调用AB方法,需要显式的调用AB

chunli@linux:~$catmain.cpp#include<iostream>usingnamespacestd;classSysA{public:voidoperationA(){cout<<"A........"<<endl;}};classSysB{public:voidoperationB(){cout<<"B........"<<endl;}};classSysC{public:voidoperationC(){cout<<"C........"<<endl;}};classSysD{public:voidoperationD(){cout<<"D........"<<endl;}};intmain(){SysAsysa;sysa.operationA();SysBsysb;sysb.operationB();return0;}chunli@linux:~$g++main.cpp&&./a.outA........B........chunli@linux:~$


现在只需要通过外观访问:打包组合起来

chunli@linux:~$catmain.cpp#include<iostream>usingnamespacestd;classSysA{public:voidoperationA(){cout<<"A........"<<endl;}};classSysB{public:voidoperationB(){cout<<"B........"<<endl;}};classSysC{public:voidoperationC(){cout<<"C........"<<endl;}};classSysD{public:voidoperationD(){cout<<"D........"<<endl;}};classFacade{public:voidmethodOne(){sysa.operationA();sysb.operationB();}voidmethodTwo(){sysc.operationC();sysd.operationD();}private:SysAsysa;SysBsysb;SysCsysc;SysDsysd;};intmain(){Facadeface;face.methodOne();return0;}chunli@linux:~$g++main.cpp&&./a.outA........B........chunli@linux:~$


适配器模式



手机充电需要使用5V电压,

创建一个适配器,将220V电压转换成5V

chunli@linux:~$catmain.cpp#include<iostream>usingnamespacestd;classV5{public:virtualvoiduseV5()=0;};//目前只有v220的类没有v5classV220{public:voiduseV220(){cout<<"使用了220v的电压"<<endl;}};//定义一个中间的适配器类classAdapter:publicV5{public:Adapter(V220*v220){this->v220=v220;}~Adapter(){if(this->v220!=NULL){deletethis->v220;}}virtualvoiduseV5(){v220->useV220();//调用需要另外的方法}private:V220*v220;};classiPhone{public:iPhone(V5*v5){this->v5=v5;}~iPhone(){if(this->v5!=NULL){deletethis->v5;}}//充电的方法voidcharge(){cout<<"iphone手机进行了充电"<<endl;v5->useV5();}private:V5*v5;};intmain(void){iPhone*phone=newiPhone(newAdapter(newV220));phone->charge();return0;}chunli@linux:~$g++main.cpp&&./a.outiphone手机进行了充电使用了220v的电压chunli@linux:~$


适配器模式中的角色和职责

Target(目标抽象类): 目标抽象类定义客户所需接口,可以是一个抽

象类或接口,也可以是具体类。

Adapter(适配器类):适配器可以调用另一个接口,作为一个转换

器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适

配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。

Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在

的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使

用的业务方法,在某些情况下可能没有适配者类的源代码。

根据对象适配器模式结构图,在对象适配器中,客户端需要调用request(

方法,而适配者类Adaptee没有该方法,但是它所提供的specificRequest()方

法却是客户端所需要的。为了使客户端能够使用适配者类,需要提供一个包装

类Adapter,即适配器类。这个包装类包装了一个适配者的实例,从而将客户

端与适配者衔接起来,在适配器的request()方法中调用适配者的

specificRequest()方法。因为适配器类与适配者类是关联关系(也可称之为委

派关系),所以这种适配器模式称为对象适配器模式。


4.4.3 适配器模式优缺点


优点:

(1) 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者

类,无须修改原有结构。

(2) 增加了类的透明性和复用性, 将具体的业务实现过程封装在适配者类

中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者

类可以在多个不同的系统中复用。

(3) 灵活性和扩展性都非常好 ,可以很方便地更换适配器,也可以在不修改

原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

缺点:

适配器中置换适配者类的某些方法比较麻烦。




适应场景

(1) 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系

统的需要,甚至没有这些类的源代码。

(2) 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的

一些类,包括一些可能在将来引进的类一起工作。