生活中的擴充卡
買筆記本電腦,買手機時,都有一個電源擴充卡,電源擴充卡又叫外置電源,是小型便攜式電子裝置及電子電器的供電電壓變換裝置,常見于手機,筆記本電腦上。它的作用是将家裡的220V高電壓轉換成這些電子産品能工作的5V~20V左右穩定的低電壓,使它們能正常工作。就是說,如果沒有這個電源擴充卡,我們的手機和電腦就不能進行充電了。
之前同僚去日本出差,由于工作需要,就将自己的筆記本帶過去了。到了的當晚就悲劇了,筆記本無法使用。由于日本的居民用電電壓是110V,而中國是220V,同僚的筆記本是220V供電的。第二天,同僚就去買了一個電壓擴充卡。如果沒有電壓擴充卡,估計這次出差都要悲劇了。
什麼是擴充卡模式?
說了這麼多生活中的擴充卡的例子,那麼在軟體設計、開發過程中,擴充卡又是個什麼東西呢?
在GOF的《設計模式:可複用面向對象軟體的基礎》中是這樣說的:将一個類的接口轉換成客戶希望的另外一個接口。擴充卡模式使得原本由于接口不相容而不能一起工作的那些類可以一起工作。好比日本現在就隻提供110V的電壓,而我的電腦就需要220V的電壓,那怎麼辦啦?擴充卡就是幹這活的,在不相容的東西之間搭建一座橋梁,讓二者能很好的相容在一起工作。
為什麼要使用擴充卡模式?
在軟體開發中,有的時候系統的資料和行為都正确,但接口不符合,我們應該考慮使用擴充卡模式,目的是使控制範圍之外的一個原有對象與某個接口比對。舉個例子:在開發一個子產品的時候,有一個功能點實作起來比較費勁,但是,之前有一個項目的子產品實作了一樣的功能點;但是現在這個子產品的接口和之前的那個子產品的接口是不一緻的。此時,作為項目經理的你,該怎麼辦啦?當然是在中間加一層Wrapper了,也就是使用擴充卡模式,将之前實作的功能點适配進新的項目了。為什麼呢?主要是使用擴充卡模式有以下優點:
- 降低了去實作一個功能點的難度,可以對現有的類進行包裝,就可以進行使用了;
- 提高了項目品質,現有的類一般都是經過測試的,使用了擴充卡模式之後,不需要對舊的類進行全面的覆寫測試;
- 總的來說,提高了效率,降低了成本。
什麼時候使用擴充卡模式?
每一種設計模式都有它最适用的場合。擴充卡模式在以下場合下最适用:
- 使用一個已經存在的類,如果它的接口和你實際要求的不一緻時,可以考慮使用擴充卡模式;
- 要在調用者和功能提供者雙方都不太容易修改的時候再使用擴充卡模式,而不是一有不同時就使用它。
UML類圖

上圖是擴充卡模式的第一種實作形式,擴充卡Adapter繼承自Target和Adaptee類,Adapter類需要重寫Target類的Request函數,在Request中做适當的處理,調用Adaptee類的SepcificRequest。最終,Target實際調用的是Adaptee的SpecificRequest來完成Request的,完成适配;這種叫做類擴充卡。
上圖是擴充卡的第二種實作形式,擴充卡Adapter類繼承自Target類,同時,在Adapter類中有一個Adaptee類型的成員變量;Adapter類重寫Request函數時,在Request中,使用Adaptee類型的成員變量調用Adaptee的SpecificRequest函數,最終完成适配;這種叫做對象擴充卡。
類擴充卡和對象擴充卡的比較
既然有了類擴充卡和對象擴充卡,那麼在實際中如何在二者之間做選擇呢?
類擴充卡有以下特點:
- 由于Adapter直接繼承自Adaptee類,是以,在Adapter類中可以對Adaptee類的方法進行重定義;
- 如果在Adaptee中添加了一個抽象方法,那麼Adapter也要進行相應的改動,這樣就帶來高耦合;
- 如果Adaptee還有其它子類,而在Adapter中想調用Adaptee其它子類的方法時,使用類擴充卡是無法做到的。
對象擴充卡有以下特點:
- 有的時候,你會發現,不是很容易去構造一個Adaptee類型的對象;
- 當Adaptee中添加新的抽象方法時,Adapter類不需要做任何調整,也能正确的進行動作;
- 可以使用多肽的方式在Adapter類中調用Adaptee類子類的方法。
由于對象擴充卡的耦合度比較低,是以在很多的書中都建議使用對象擴充卡。在我們實際項目中,也是如此,能使用對象組合的方式,就不使用多繼承的方式。
代碼實作
類擴充卡的實作代碼
1 #include <iostream>
2 using namespace std;
3
4 // Targets
5 class Target
6 {
7 public:
8 virtual void Request()
9 {
10 cout<<"Target::Request"<<endl;
11 }
12 };
13
14 // Adaptee
15 class Adaptee
16 {
17 public:
18 void SpecificRequest()
19 {
20 cout<<"Adaptee::SpecificRequest"<<endl;
21 }
22 };
23
24 // Adapter
25 class Adapter : public Target, Adaptee
26 {
27 public:
28 void Request()
29 {
30 Adaptee::SpecificRequest();
31 }
32 };
33
34 // Client
35 int main(int argc, char *argv[])
36 {
37 Target *targetObj = new Adapter();
38 targetObj->Request();
39
40 delete targetObj;
41 targetObj = NULL;
42
43 return 0;
44 }
對象擴充卡的代碼實作
1 #include <iostream>
2 using namespace std;
3
4 class Target
5 {
6 public:
7 Target(){}
8 virtual ~Target(){}
9 virtual void Request()
10 {
11 cout<<"Target::Request"<<endl;
12 }
13 };
14
15 class Adaptee
16 {
17 public:
18 void SpecificRequest()
19 {
20 cout<<"Adaptee::SpecificRequest"<<endl;
21 }
22 };
23
24 class Adapter : public Target
25 {
26 public:
27 Adapter() : m_Adaptee(new Adaptee) {}
28
29 ~Adapter()
30 {
31 if (m_Adaptee != NULL)
32 {
33 delete m_Adaptee;
34 m_Adaptee = NULL;
35 }
36 }
37
38 void Request()
39 {
40 m_Adaptee->SpecificRequest();
41 }
42
43 private:
44 Adaptee *m_Adaptee;
45 };
46
47 int main(int argc, char *argv[])
48 {
49 Target *targetObj = new Adapter();
50 targetObj->Request();
51
52 delete targetObj;
53 targetObj = NULL;
54
55 return 0;
56 }
總結
擴充卡模式很容易了解和實作,在以後的項目中,多多的進行實踐,将學到的理論知識運用到實際的項目中去,寫出漂亮的代碼。