基本信息
共有兩類適配器模式:
對象適配器模式
-- 在這種適配器模式中,適配器容納一個它包裹的類的實例。在這種情況下,適配器調用被包裹對象的物理實體。
類適配器模式
-- 這種適配器模式下,適配器繼承自已實現的類(一般多重繼承)。
解釋
將一個類的接口轉換成客戶希望的另外一個接口。 Adapter模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作。
基本概念
客戶:需要調用我們的代碼的對象。
Adapter模式的宗旨:保留現有類所提供的服務,向客戶提供接口,以滿足客戶的期望。
主要內容
(1)類適配器:
當客戶在接口中定義了他期望的行為時,我們就可以套用適配器模式,提供一個實現該接口的類,並且擴展已有的類,通過創建子類來實現適配。
下面是類適配器的UML圖:
(2)對象適配器:
對象適配器”通過組合除了滿足“用戶期待接口”還降低了代碼間的不良耦合。在工作中推薦使用“對象適配”。下面是對象適配器的UML圖:
(3) 預設適配器模式:
預設適配器模式是一種特殊的適配器模式,但這個適配器是由一個抽象類實現的,並且在抽象類中要實現目標接口中所規定的所有方法,但很多方法的實現都是“平庸”的實現,也就是說,這些方法都是空方法。而具體的子類都要繼承此抽象類。
C++樣例
類適配器樣例
#include<iostream>
using namespace std;
// "ITarget"
class Target
{
public:
// Methods
virtual void Request(){};
};
// "Adaptee"
class Adaptee
{
public:
// Methods
void SpecificRequest()
{
cout<<"Called SpecificRequest()"<<endl;
}
};
// "Adapter"
class Adapter : public Adaptee, public Target
{
public:
// Implements ITarget interface
void Request()
{
// Possibly do some data manipulation
// and then call SpecificRequest
this->SpecificRequest();
}
};
int main()
{
// Create adapter and place a request
Target *t = new Adapter();
t->Request();
return 0;
}
對象適配器樣例
#include<iostream>
using namespace std;
// "ITarget"
class Target
{
public:
// Methods
virtual void Request(){};
};
// "Adaptee"
class Adaptee
{
public:
// Methods
void SpecificRequest()
{
cout<<"Called SpecificRequest()"<<endl;
}
};
// "Adapter"
class Adapter : public Target
{
private:
Adaptee *adaptee;
public:
Adapter()
{
adaptee = new Adaptee();
}
// Implements ITarget interface
void Request()
{
// Possibly do some data manipulation
// and then call SpecificRequest
adaptee->SpecificRequest();
}
};
int main()
{
// Create adapter and place a request
Target *t = new Adapter();
t->Request();
return 0;
}
預設適配器樣例
#include<iostream>
using namespace std;
class Target {
public:
virtual void f1(){};
virtual void f2(){};
virtual void f3(){};
};
class DefaultAdapter : public Target
{
public:
void f1() {
}
void f2() {
}
void f3() {
}
};
class MyInteresting :public DefaultAdapter
{
public:
void f3(){
cout<<"呵呵,我就對f3()方法感興趣,別的不管了!"<<endl;
}
};
int main()
{
// Create adapter and place a request
Target *t = new MyInteresting();
t->f3();
return 0;
}
適用情況
使用的前提:
1.接口中規定了所有要實現的方法
2.但一個要實現此接口的具體類,只用到了其中的幾個方法,而其它的方法都是沒有用的。
實現方法
1.用一個抽象類實現已有的接口,並實現接口中所規定的所有方法,這些方法的實現可以都是“平庸”實現----空方法;但此類中的方法是具體的方法,而不是抽象方法,否則的話,在具體的子類中仍要實現所有的方法,這就失去了適配器本來的作用。
2.原本要實現接口的子類,只實現1中的抽象類即可,並在其內部實現時,只對其感興趣的方法進行實現。
注意事項
1.充當適配器角色的類就是:實現已有接口的抽象類
2.為什麼要用抽象類:
此類是不要被實例化的。而只充當適配器的角色,也就為其子類提供了一個共同的接口,但其子類又可以將精力只集中在其感興趣的地方。
模式解析
你想使用一個已經存在的適配器模式,而他的接口不符合你的需求。你想創建一個可以復用的類,該類可以與其他不相關的類或不可預見的類協同工作。你想使用一些已經存在的子類,但是不可能對每一個都進行子類化已一匹配他們的接口,對象適配器可以適配他的父類接口。 適配器如同一個常見的變壓器,也如同電腦的變壓器和插線板之間的電源連線線,他們雖然都是3相的,但是電腦後面的插孔卻不能直接插到插線板上。 作者曾經遇到過一個ASP編程的難題,asp不是面向對象的,但是卻可以借鑑適配器模式解決問題。問題是這樣的,在一個產品表(product)中的所有產品都有一個編號,欄位名字是bh,每個編號是唯一的,但卻不是主鍵,表中使用一個自動增長的id作為主鍵。在產品的詳情頁中使用傳過來的參數id查詢產品,而在另外的一個系統中也有一個同樣的表,需要訪問詳情頁(已經由另外的一個程式設計師設計好,並且代碼晦澀難懂),由於欄位值是自動增長的,兩個表中的主鍵並不對應(在其中的一個系統中進行刪除添加都會引起id的增長),在具體的實現中,本人在有詳情頁的系統中添加了一個頁面(adapter),接受傳過來的產品編號bh,然後根據編號查找資料庫得到相應產品的駐鍵id,最後讓頁面跳轉到詳情頁並傳遞一個id,在另外的系統中只要得到產品的編號bh,並把bh作為參數傳遞到添加的頁面(adapter)便可以得到正確的結果。
總結
個人經驗
如何做到一個類不被實例化或者不被輕易實例化?
1.把一個類定義為抽象類;
2.把一個類的構造方法設定為:private類型的,這樣在客戶端就不能通過new ClassName()方法來輕易將一個類實例化,而要生成此類的實例就必須通過一個特殊的方法,這樣在一個系統中,對此類的使用就能得到合理的控制(如:單例模式/多例模式/簡單工廠方法等模式)。
3. 對於兩個獨立的系統,要滿足ocp原則,則適配器模式會有一定的局限性。
專業術語解釋
ps:顯示卡(video card,Graphics card),又稱為顯示適配器(video Adapter)。