第27章 結構型模式大PK
27.1 代理模式 VS 裝飾模式
27.1.1 代理模式
(1)場景:客人找運動員代理要求安排運動員參加比賽
(2)說明:代理人有控制權,可以拒絕客人的要求,也可以答應安排,甚至自己下去跑(因為有些運動員本身就作自己的代理)
【程式設計實驗】找代理安排運動員比賽
//結構型模式大PK——代理模式和裝飾模式 //例項:找代理安排運動員比賽 #include <iostream> #include <ctime> using namespace std; //抽象運動員 class IRunner { public: virtual void run() = 0; virtual ~IRunner() {} }; //運動員 class Runner : public IRunner { public: void run() { cout << "運動員跑步,動作很瀟灑" << endl; } }; //代理人(也與運動員一樣,實現同一介面) class RunnerAgent : public IRunner { Runner* runner; public: RunnerAgent(Runner* runner) { srand((int)time(NULL)); this->runner = runner; } void run() { if((rand() % 2) ==0) { cout << "代理人同意安排運動員跑步" << endl; runner->run(); } else { cout << "代理人心情不好,不安排運動員跑步" << endl; } } }; int main() { //定義一個運動員 Runner* runner = new Runner(); //定義代理人 IRunner* agent = new RunnerAgent(runner); //要求運動員跑步 cout << "===客人找到代理要求運動員去跑步" << endl; //為演示,比如洽談了10次 for(int i=0; i<10; i++) { cout <<"第" <<i+1 <<"次洽談結果:"; agent->run(); cout << endl; } delete runner; delete agent; return 0; };
27.1.2 裝飾模式
(1)裝飾模式:對類的功能進行加強
【程式設計實驗】安裝噴氣裝置
//結構型模式大PK——代理模式和裝飾模式 //例項:為飛機安裝噴氣動力裝置 #include <iostream> #include <ctime> using namespace std; //飛行介面 class IFly { public: virtual void fly() = 0; virtual ~IFly() {} }; //飛機 class AirPlane : public IFly { public: void fly() { cout << "飛機正在飛行..." << endl; } }; //JetFly class JetFly : public IFly { IFly* airPlane; public: JetFly(IFly* airPlane) { this->airPlane = airPlane; } void speedUp() { cout << "為飛機加裝了噴氣動力裝置,正在加速中..." << endl; } void fly() { speedUp(); airPlane->fly(); } }; int main() { //定義一架飛機 IFly* airPlane = new AirPlane(); //定義裝飾類,對功能進行加強 IFly* jet = new JetFly(airPlane); cout << "===增加功能後的飛機" << endl; jet->fly(); delete jet; delete airPlane; return 0; };
27.1.3 最佳實踐
(1)裝飾類對被裝飾的類的行為沒有決定權,只有增強作用。而代理可以控制對被代理人的訪問。
(2)代理模式是把當前的行為或功能委託給其他物件執行,代理類負責介面限定(如是否可以呼叫真實角色,一般不對被代理功能當修飾,而是保證原汁原葉的呼叫。
(3)裝飾模式在保證介面不變的情況下加強類的功能,它保證的是被修飾物件功能比原來豐富(當然也可以減弱),但不做准入條件和准入引數的過濾。
27.1.3 最佳實踐
(1)裝飾類對被裝飾的類的行為沒有決定權,只有增強作用。而代理可以控制對被代理人的訪問。
(2)代理模式是把當前的行為或功能委託給其他物件執行,代理類負責介面限定(如是否可以呼叫真實角色,一般不對被代理功能當修飾,而是保證原汁原葉的呼叫
(3)裝飾模式在保證介面不變的情況下加強類的功能,它保證的是被修飾物件功能比原來豐富(當然也可以減弱),但不做准入條件和准入引數的過濾。
27.2 裝飾模式 VS 介面卡模式
27.2.1 用裝飾模式描述醜小鴨
(1)醜小鴨首先是一隻天鵝,具備了天鵝所有的行為和屬性(即它從天鵝繼承而來)
(2)只是小時候,又小、又髒,又不能飛行。隨著時間推移,對其屬性和行為進行加強,慢慢變成一隻白天鵝。
【程式設計實驗】醜小鴨變小天鵝的故事
//結構型模式大PK——裝飾模式和介面卡模式
//例項:醜小鴨變小天鵝
#include <iostream>
#include <ctime>
using namespace std;
//天鵝介面
class Swan
{
public:
virtual void fly() = 0; //會飛
virtual void cry() = 0; //會叫
virtual void Appearance() = 0; //外觀
virtual ~Swan(){}
};
//醜小鴨(本質上是天鵝)
class UglyDuckling : public Swan
{
public:
//醜小鴨還比較小,不能飛
void fly()
{
cout << "不能飛" <<endl;
}
//醜小鴨的叫聲
void cry()
{
cout << "叫聲是克嚕——克嚕——克嚕" << endl;
}
//醜小鴨的外形
void Appearance()
{
cout << "外形是髒兮兮的白色,毛毛茸茸的大腦袋" <<endl;
}
};
//裝飾類
class Decorator : public Swan
{
private:
Swan* swan;
public:
Decorator(Swan* swan)
{
this->swan = swan;
}
void fly()
{
swan->fly();
}
void cry()
{
swan->cry();
}
void Appearance()
{
swan->Appearance();
}
};
//具體的裝飾類(外形美化)
class BeautifyAppearance : public Decorator
{
public:
BeautifyAppearance(Swan* swan):Decorator(swan){}
//外表美化處理(重寫該方法)
void Appearance()
{
//這裡是重新實現,不再呼叫原來的Decorator::desAppearance()
cout << "外表是純白色的,非常惹人喜愛" << endl;
}
};
//具體裝飾類
class StrongBehavior :public Decorator
{
public:
StrongBehavior(Swan* swan):Decorator(swan){}
//會飛行了
void fly()
{
//這裡是完全重新實現了被裝飾類飛的功能。
cout << "會飛行了!" << endl;
}
};
int main()
{
cout <<"===很久很久以前,這裡有一隻醜陋的小鴨子===" << endl;
Swan* duckling = new UglyDuckling();
//展示一下小鴨
duckling->Appearance();
duckling->cry();
duckling->fly();
cout <<"===小鴨子終於發現自己是一隻天鵝===" << endl;
//首先外形變化
BeautifyAppearance duckling2(duckling);
StrongBehavior duckling3(&duckling2);
duckling3.Appearance();
duckling3.cry();
duckling3.fly();
delete duckling;
return 0;
};
/*輸出結果:
===很久很久以前,這裡有一隻醜陋的小鴨子===
外形是髒兮兮的白色,毛毛茸茸的大腦袋
叫聲是克嚕——克嚕——克嚕
不能飛
===小鴨子終於發現自己是一隻天鵝===
外表是純白色的,非常惹人喜愛
叫聲是克嚕——克嚕——克嚕
會飛行了!
*/
27.2.2 用介面卡模式描述醜小鴨
(1)兩個介面:鴨和天鵝。而醜小鴨就相當於介面卡。
(2)為了演示介面卡的使用,醜小鴨本質上是天鵝,所以應該把鴨子的介面轉為天鵝的介面。即鴨子的源介面,目標介面為天鵝。以便讓天鵝具備小鴨的一些特點。
【程式設計實驗】一隻與眾不同的鴨子
//結構型模式大PK——裝飾模式和介面卡模式
//例項:一隻與眾不同的鴨子
#include <iostream>
#include <ctime>
using namespace std;
//天鵝介面
class Swan
{
public:
virtual void behavior() = 0; //其他行為,會飛
virtual void cry() = 0; //會叫
virtual void Appearance() = 0; //外觀
};
//鴨子的介面
class Duck
{
public:
virtual void behavior() = 0; //會游泳
virtual void cry() = 0; //會叫
virtual void Appearance() = 0; //外觀
virtual ~Duck(){}
};
//白天鵝
class WhiteSwan : public Swan
{
public:
void cry()
{
cout << "叫聲是克嚕——克嚕——克嚕" << endl;
}
void Appearance()
{
cout << "外形是純白色,惹人喜愛" << endl;
}
//其他行為
void behavior()
{
cout << "能夠飛行" << endl;
}
};
//小鴨子
class Duckling : public Duck
{
public:
void cry()
{
cout << "叫聲是嘎——嘎——嘎" << endl;
}
void Appearance()
{
cout << "外形是黃白相間,嘴長" << endl;
}
//描述鴨子的其他行為
void behavior()
{
cout << "會游泳" << endl;
}
};
//醜小鴨相當於介面卡的角色,醜小鴨本質上是天鵝,所以應該把鴨子
//的介面轉為天鵝的介面。即鴨子的源介面,目標介面為天鵝
class UglyDuckling : public WhiteSwan
{
Duck* duck; //目標介面
public:
UglyDuckling(Duck* duck)
{
this->duck = duck;
}
//醜小鴨的叫聲,直接從父類繼承
//醜小鴨的外形,直接從父類繼承
//其他行為
void behavior()
{
//本身會飛
WhiteSwan::behavior();
//學會鴨子的游泳
duck->behavior();
}
};
int main()
{
cout <<"===鴨媽媽有5個孩子,其中4個都是一個模樣==="<< endl;
Duck* duck = new Duckling();
duck->cry();
duck->Appearance();
duck->behavior();
cout <<"===一隻獨特的小鴨子,模樣是這樣的==="<< endl;
UglyDuckling uglyDuckling(duck); //醜小鴨
uglyDuckling.cry();
uglyDuckling.Appearance();
uglyDuckling.behavior();
delete duck;
return 0;
};
/*輸出結果:
===鴨媽媽有5個孩子,其中4個都是一個模樣===
叫聲是嘎——嘎——嘎
外形是黃白相間,嘴長
會游泳
===一隻獨特的小鴨子,模樣是這樣的===
叫聲是克嚕——克嚕——克嚕
外形是純白色,惹人喜愛
能夠飛行
會游泳
*/
27.2.3 最佳實踐
(1)裝飾模式包裝的是自己的兄弟類,隸屬於同一家族(相同的父類),而介面卡模式則修飾的是非血緣關係類。把一個非本家族偽裝成本家族的物件,注意是偽裝,因此它的本質還是不同的介面物件。
(2)意圖不同
裝飾模式的意圖是加強物件的功能,而介面卡模式關注的是轉化,兩個物件之間的介面轉化。
(3)所作用的物件不同
①裝飾模式裝飾的物件必須是自己的同宗,也就是相同的父類,只要在具有相同的屬性和行為情況下,才能比較行為是增加或減弱。
②介面卡模式則必須是兩個不物件介面物件,因為它著重於轉換
(4)場景不同
①裝飾模式只要是想增強功能的,都可以用。
②介面卡模式則是一個補救模式,一般出現在系統成熟或己經構建完畢的專案中,作為一個緊急處理手段採用。
(5)擴充套件性不同
裝飾模式容易擴充套件,如果不需要修飾某物件了,可以隨時拿掉,而且裝飾類也可以繼續擴充套件。但介面卡模式就不同,它在兩個不同物件之間架起一座溝通的橋染,建立容易,去掉時需要從系統整體考慮是否能夠撤銷。