基本信息,解釋,基本概念,主要內容,C++樣例,類適配器樣例,對象適配器樣例,預設適配器樣例,適用情況,實現方法,注意事項,模式解析,總結,個人經驗,專業術語解釋,
基本信息
共有兩類適配器模式:
對象適配器模式
-- 在這種適配器模式中,適配器容納一個它包裹的類的實例。在這種情況下,適配器調用被包裹對象的物理實體。
類適配器模式
-- 這種適配器模式下,適配器繼承自已實現的類(一般多重繼承)。
解釋
將一個類的接口轉換成客戶希望的另外一個接口。
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)。