C++设计模式——适配器模式(对象适配器、类适配器)
http://blog.jobbole.com/109381/?winzoom=1
前言?
? ? 現(xiàn)在的筆記本都只存在USB接口,用來口,電腦上存在的是USB接口,兩者如何通信呢?可以使用USB轉(zhuǎn)串口線連接電腦和嵌入式設(shè)備,其中USB一端連接電腦、串口和外部設(shè)備進(jìn)行通信。然而在一些嵌入式設(shè)備上(例如ARM9),通常使用串口和電腦進(jìn)行通信。嵌入式設(shè)備上存在的是串一端連接嵌入式設(shè)備。本來電腦和嵌入式設(shè)備由于接口不兼容,無法進(jìn)行通信,而使用USB轉(zhuǎn)串口線這個(gè)適配器,兩者之間就可以正常進(jìn)行數(shù)據(jù)通信。在設(shè)計(jì)模式中,也存在一種類似的模式,存在兩個(gè)接口不同的類,可以使用一個(gè)適配器類來將一個(gè)接口轉(zhuǎn)換為客戶希望的另一個(gè)接口,稱為適配器模式。
1、適配器模式
? ? 在適配器模式中引入了一個(gè)被稱為適配器(Adapter)的包裝類,而它所包裝的對(duì)象稱為適配者(Adaptee),即被適配的類。適配器的實(shí)現(xiàn)就是把客戶類的請(qǐng)求轉(zhuǎn)化為對(duì)適配者的相應(yīng)接口的調(diào)用。也就是說:當(dāng)客戶類調(diào)用適配器的方法時(shí),在適配器類的內(nèi)部將調(diào)用適配者類的方法,而這個(gè)過程對(duì)客戶類是透明的,客戶類并不直接訪問適配者類。因此,適配器讓那些由于接口不兼容而不能交互的類可以一起工作。
? ? 適配器模式可以將一個(gè)類的接口和另一個(gè)類的接口匹配起來,而無須修改原來的適配者接口和抽象目標(biāo)類接口。適配器模式定義如下:
適配器模式(Adapter Pattern):將一個(gè)接口轉(zhuǎn)換成客戶希望的另一個(gè)接口,使接口不兼容的那些類可以一起工作,其別名為包裝器(Wrapper)。適配器模式既可以作為類結(jié)構(gòu)型模式,也可以作為對(duì)象結(jié)構(gòu)型模式。 |
? ? 在適配器模式中,我們通過增加一個(gè)新的適配器類來解決接口不兼容的問題,使得原本沒有任何關(guān)系的類可以協(xié)同工作。根據(jù)適配器類與適配者類的關(guān)系不同,適配器模式可分為對(duì)象適配器和類適配器兩種,在對(duì)象適配器模式中,適配器與適配者之間是關(guān)聯(lián)關(guān)系;在類適配器模式中,適配器與適配者之間是繼承(或?qū)崿F(xiàn))關(guān)系。在實(shí)際開發(fā)中,對(duì)象適配器的使用頻率更高。
? ? ? ? ? ? ??對(duì)象適配器模式結(jié)構(gòu)圖
? ? 在對(duì)象適配器模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:
? ??Target(目標(biāo)抽象類):目標(biāo)抽象類定義客戶所需接口,可以是一個(gè)抽象類或接口,也可以是具體類。
? ??Adapter(適配器類):適配器可以調(diào)用另一個(gè)接口,作為一個(gè)轉(zhuǎn)換器,對(duì)Adaptee和Target進(jìn)行適配,適配器類是適配器模式的核心,在對(duì)象適配器中,它通過繼承Target并關(guān)聯(lián)一個(gè)Adaptee對(duì)象使二者產(chǎn)生聯(lián)系。
? ??Adaptee(適配者類):適配者即被適配的角色,它定義了一個(gè)已經(jīng)存在的接口,這個(gè)接口需要適配,適配者類一般是一個(gè)具體類,包含了客戶希望使用的業(yè)務(wù)方法,在某些情況下可能沒有適配者類的源代碼。
? ? 根據(jù)對(duì)象適配器模式結(jié)構(gòu)圖,在對(duì)象適配器中,客戶端需要調(diào)用request()方法,而適配者類Adaptee沒有該方法,但是它所提供的specificRequest()方法卻是客戶端所需要的。為了使客戶端能夠使用適配者類,需要提供一個(gè)包裝類Adapter,即適配器類。這個(gè)包裝類包裝了一個(gè)適配者的實(shí)例,從而將客戶端與適配者銜接起來,在適配器的request()方法中調(diào)用適配者的specificRequest()方法。因?yàn)檫m配器類與適配者類是關(guān)聯(lián)關(guān)系(也可稱之為委派關(guān)系),所以這種適配器模式稱為對(duì)象適配器模式。
2、Socket網(wǎng)絡(luò)通信的設(shè)計(jì)與實(shí)現(xiàn)------對(duì)象適配器
? ? 使用TCP進(jìn)行網(wǎng)絡(luò)通信,一般都會(huì)包含創(chuàng)建套接字、綁定套接字、監(jiān)聽套接字、連接套接字等4個(gè)過程。某網(wǎng)絡(luò)公司已經(jīng)有一套成熟的套接字軟件包,里面包含網(wǎng)絡(luò)通信的4個(gè)過程。現(xiàn)欲開發(fā)一套新的聊天軟件,決定復(fù)用已經(jīng)成熟的套接字軟件包。
?
? ? SocketPackage就是成熟的套接字軟件包,也就是Adaptee適配者類,包含創(chuàng)建套接字、綁定套接字、監(jiān)聽套接字、連接套接字等成熟方法; Socket是目標(biāo)抽象類Target,定義了客戶希望的方法;?SocketAdapter為適配器類,將成熟軟件包里面的方法轉(zhuǎn)為Socket類中客戶希望的方法。
2.1 待適配的類——成熟的套接字
? ? 成熟的套接字軟件包實(shí)現(xiàn)代碼如下:
[cpp]?view plain?copy
#ifndef?_SOCKET_PACKAGE_H_??
#define?_SOCKET_PACKAGE_H_??
??
#include?<iostream>??
#include?<string>??
using?namespace?std;??
??
??
//套接字包(可以被復(fù)用)??
class?SocketPackage??
{??
public:??
????void?CreateSpecificSocket()??
????{??
????????cout?<<?"創(chuàng)建套接字"?<<?endl;??
????}??
??
????void?BindSpecificSocket()??
????{??
????????cout?<<?"綁定套接字"?<<?endl;??
????}??
??
????void?ListenSpecificSocket()??
????{??
????????cout?<<?"監(jiān)聽套接字"?<<?endl;??
????}??
??
????void?ConnecSpecifictSocket()??
????{??
????????cout?<<?"連接套接字"?<<?endl;??
????}??
??
};??
??
#endif??
?現(xiàn)欲開發(fā)的聊天軟件定義了一個(gè)抽象類Socket,也就是目標(biāo)抽象類Target,里面包含創(chuàng)建套接字、綁定套接字、監(jiān)聽套接字、連接套接字等客戶希望的方法,但這些方法和成熟軟件包中的方法不同。如:Socket類創(chuàng)建套接字方法為CreateSocket,而SocketPackage類創(chuàng)建套接字方法為CreateSpecificSocket。這兩個(gè)方法不同,無法進(jìn)行通信,為了復(fù)用成熟套接字軟件包里面的方法,可以提供一個(gè)適配器類,繼承于抽象的Socket類。在適配器中的方法將調(diào)用套接字軟件包里面的方法。
2.2 適配器類——新的套接字
? ? 套接字適配器類實(shí)現(xiàn)代碼如下:
[cpp]?view plain?copy
#ifndef?_SOCKET_H_??
#define?_SOCKET_H_??
??
#include?"SocketPackage.h"??
??
//抽象套接字類??
class?Socket??
{??
public:??
????//創(chuàng)建套接字??
????virtual?void?CreateSocket()?=?0;??
??
????//綁定套接字??
????virtual?void?BindSocket()?=?0;??
??
????//監(jiān)聽套接字??
????virtual?void?ListenSocket()?=?0;??
??
????//連接套接字??
????virtual?void?ConnectSocket()?=?0;??
};??
??
??
??
//套接字適配器??
class?SocketAdapter?:?public?Socket??
{??
private:??
????SocketPackage?*?m_pSocketPackage;??
public:??
????//構(gòu)造函數(shù),創(chuàng)建一個(gè)需要復(fù)用的套接字包對(duì)象??
????SocketAdapter()??
????{??
????????m_pSocketPackage?=?new?SocketPackage();??
????}??
??
????//銷毀需要復(fù)用的套接字包對(duì)象??
????~SocketAdapter()??
????{??
????????if(?NULL?!=?m_pSocketPackage?)??
????????{??
????????????delete?m_pSocketPackage;??
????????????m_pSocketPackage?=?NULL;??
????????}??
????}??
??
????//創(chuàng)建套接字??
????void?CreateSocket()??
????{??
????????m_pSocketPackage->CreateSpecificSocket();??
????}??
??????
????//綁定套接字??
????void?BindSocket()??
????{??
????????m_pSocketPackage->BindSpecificSocket();??
????}??
??????
????//監(jiān)聽套接字??
????void?ListenSocket()??
????{??
????????m_pSocketPackage->ListenSpecificSocket();??
????}??
??????
????//連接套接字??
????void?ConnectSocket()??
????{??
????????m_pSocketPackage->ConnecSpecifictSocket();??
????}??
};??
??
??
#endif??
2.3 測(cè)試
測(cè)試代碼實(shí)現(xiàn)如下:
[cpp]?view plain?copy
#include?<iostream>??
#include?"Socket.h"??
??
??
using?namespace?std;??
??
??
int?main()??
{??
????//創(chuàng)建套接字適配器對(duì)象??
????Socket?*?pSocketAdapter?=?new?SocketAdapter();??
??????
????//使用適配器進(jìn)行套接字操作??
????pSocketAdapter->CreateSocket();??
????pSocketAdapter->BindSocket();??
????pSocketAdapter->ListenSocket();??
????pSocketAdapter->ConnectSocket();??
??
??
????//銷毀操作??
????delete?pSocketAdapter;??
????pSocketAdapter?=?NULL;??
??????
????return?0;??
}??
編譯并執(zhí)行,結(jié)果如下:
? ??SocketAdapter套接字適配器類和SocketPackage套接字包是一種組合的關(guān)系,當(dāng)調(diào)用套接字適配器類中的相應(yīng)方法時(shí),將調(diào)用套接字包對(duì)應(yīng)的方法,通過組合的方式,實(shí)現(xiàn)對(duì)套接字軟件包模塊的復(fù)用。客戶端不需要直接操作這個(gè)已經(jīng)成熟的套接字軟件包,而是由套接字適配器類進(jìn)行委托操作,降低了客戶端和適配者類的耦合。
? ? 對(duì)象適配器模式中,適配器類Adapter和適配者類Adatpee類是一種關(guān)聯(lián)關(guān)系,或者組合關(guān)系。適配器類維護(hù)一個(gè)適配者類的引用,在適配器的方法中調(diào)用相應(yīng)的適配者類中的方法,實(shí)現(xiàn)對(duì)適配者功能的復(fù)用,這個(gè)過程對(duì)客戶端是透明的,客戶類不直接訪問適配者類。
3、Socket網(wǎng)絡(luò)通信的設(shè)計(jì)與實(shí)現(xiàn)------類適配器
除了對(duì)象適配器模式之外,適配器模式還有一種形式,那就是類適配器模式,類適配器模式和對(duì)象適配器模式最大的區(qū)別在于適配器和適配者之間的關(guān)系不同,對(duì)象適配器模式中適配器和適配者之間是關(guān)聯(lián)關(guān)系,而類適配器模式中適配器和適配者是繼承關(guān)系。不管是對(duì)象適配器還是類適配器,都是為了復(fù)用適配者類的功能。
? ? SocketPackage就是成熟的套接字軟件包,也就是Adaptee適配者類,包含創(chuàng)建套接字、綁定套接字、監(jiān)聽套接字、連接套接字等成熟方法; Socket是目標(biāo)抽象類Target,定義了客戶希望的方法;?SocketAdapter為適配器類,繼承自Socket類和SocketPackage類,將成熟軟件包里面的方法轉(zhuǎn)為Socket類中客戶希望的方法。
3.1 帶適配類——成熟套接字
? ??成熟的套接字軟件包實(shí)現(xiàn)代碼如下:
[cpp]?view plain?copy
#ifndef?_SOCKET_PACKAGE_H_??
#define?_SOCKET_PACKAGE_H_??
??
#include?<iostream>??
#include?<string>??
using?namespace?std;??
??
??
//套接字包(可以被復(fù)用)??
class?SocketPackage??
{??
public:??
????void?CreateSpecificSocket()??
????{??
????????cout?<<?"創(chuàng)建套接字"?<<?endl;??
????}??
??
????void?BindSpecificSocket()??
????{??
????????cout?<<?"綁定套接字"?<<?endl;??
????}??
??
????void?ListenSpecificSocket()??
????{??
????????cout?<<?"監(jiān)聽套接字"?<<?endl;??
????}??
??
????void?ConnecSpecifictSocket()??
????{??
????????cout?<<?"連接套接字"?<<?endl;??
????}??
??
};??
??
#endif??
SocketAdapter套接字適配器類和SocketPackage套接字包類不在是組合的關(guān)系,SocketAdapter繼承于Socket類和SocketPackage,當(dāng)調(diào)用SocketAdapter套接字適配器類中的方法,將會(huì)調(diào)用其基類SocketPackage的相應(yīng)方法,通過繼承的方式實(shí)現(xiàn)對(duì)適配者類的功能復(fù)用。
3.2 適配類——采用(多)繼承
? ? 套接字適配器類實(shí)現(xiàn)代碼如下:
[cpp]?view plain?copy
#ifndef?_SOCKET_H_??
#define?_SOCKET_H_??
??
#include?"SocketPackage.h"??
??
//抽象套接字類??
class?Socket??
{??
public:??
????//創(chuàng)建套接字??
????virtual?void?CreateSocket()?=?0;??
??
????//綁定套接字??
????virtual?void?BindSocket()?=?0;??
??
????//監(jiān)聽套接字??
????virtual?void?ListenSocket()?=?0;??
??
????//連接套接字??
????virtual?void?ConnectSocket()?=?0;??
};??
??
??
??
//套接字適配器(類適配器)??
class?SocketAdapter?:?public?Socket,?SocketPackage??
{??
public:??
??
????//創(chuàng)建套接字??
????void?CreateSocket()??
????{??
????????CreateSpecificSocket();??
????}??
??????
????//綁定套接字??
????void?BindSocket()??
????{??
????????BindSpecificSocket();??
????}??
??????
????//監(jiān)聽套接字??
????void?ListenSocket()??
????{??
????????ListenSpecificSocket();??
????}??
??????
????//連接套接字??
????void?ConnectSocket()??
????{??
????????ConnecSpecifictSocket();??
????}??
};??
??
??
#endif??
3.3 測(cè)試
測(cè)試程序?qū)崿F(xiàn)代碼如下:
[cpp]?view plain?copy
#include?<iostream>??
#include?"Socket.h"??
??
using?namespace?std;??
??
int?main()??
{??
????//創(chuàng)建套接字適配器對(duì)象??
????Socket?*?pSocketAdapter?=?new?SocketAdapter();??
??????
????//使用適配器進(jìn)行套接字操作??
????pSocketAdapter->CreateSocket();??
????pSocketAdapter->BindSocket();??
????pSocketAdapter->ListenSocket();??
????pSocketAdapter->ConnectSocket();??
??
????//銷毀操作??
????delete?pSocketAdapter;??
????pSocketAdapter?=?NULL;??
??????
????return?0;??
}??
編譯并執(zhí)行,程序結(jié)果如下:
? ? 適配器類繼承于目標(biāo)抽象類和適配者類,當(dāng)調(diào)用適配器類的方法時(shí),在該方法內(nèi)部將調(diào)用其基類,也就是適配者類中相應(yīng)的方法,通過繼承的方式,實(shí)現(xiàn)復(fù)用適配者類的功能。
? ? 不管是對(duì)象適配器還是類適配器,都是為了實(shí)現(xiàn)功能的復(fù)用。
4、適配器模式總結(jié)
? ??在對(duì)象適配器模式中,適配器與適配者之間是關(guān)聯(lián)關(guān)系;在類適配器模式中,適配器與適配者之間是繼承關(guān)系。不論是對(duì)象適配器還是類適配器,適配器模式都將現(xiàn)有接口轉(zhuǎn)化為客戶類所期望的接口,實(shí)現(xiàn)了對(duì)現(xiàn)有類的復(fù)用。為了避免兩個(gè)或者多個(gè)事物之間直接耦合,應(yīng)該如何分配職責(zé)? 大多數(shù)情況都可以通過增加一層間接性來解決,將職責(zé)分配給中介對(duì)象,使其作為其他構(gòu)件或服務(wù)之間的媒介,以避免他們之間的直接耦合。在適配器模式中,為了避免客戶類和與適配者類直接耦合,也就是客戶類直接訪問適配者類,可以增加一個(gè)適配器類,降低客戶類與適配者類的耦合性。當(dāng)客戶類調(diào)用適配器的方法時(shí),在適配器類的內(nèi)部將調(diào)用適配者類的方法,而這個(gè)過程對(duì)客戶類是透明的,客戶類并不直接訪問適配者類。它是一種使用頻率非常高的設(shè)計(jì)模式,在軟件開發(fā)中得以廣泛應(yīng)用。
4.1 主要優(yōu)點(diǎn)
? ? 無論是對(duì)象適配器模式還是類適配器模式都具有如下優(yōu)點(diǎn):
? ? (1)?將目標(biāo)類和適配者類解耦,通過引入一個(gè)適配器類來重用現(xiàn)有的適配者類,無須修改原有結(jié)構(gòu)。
? ? (2)?增加了類的透明性和復(fù)用性,將具體的業(yè)務(wù)實(shí)現(xiàn)過程封裝在適配者類中,對(duì)于客戶端類而言是透明的,而且提高了適配者的復(fù)用性,同一個(gè)適配者類可以在多個(gè)不同的系統(tǒng)中復(fù)用。
? ? (3)?靈活性和擴(kuò)展性都非常好,通過使用配置文件,可以很方便地更換適配器,也可以在不修改原有代碼的基礎(chǔ)上增加新的適配器類,完全符合“開閉原則”。
? ? 具體來說,類適配器模式還有如下優(yōu)點(diǎn):
? ? 由于適配器類是適配者類的子類,因此可以在適配器類中置換一些適配者的方法,使得適配器的靈活性更強(qiáng)。
? ? 對(duì)象適配器模式還有如下優(yōu)點(diǎn):
? ? (1)?一個(gè)對(duì)象適配器可以把多個(gè)不同的適配者適配到同一個(gè)目標(biāo);
? ? (2)?可以適配一個(gè)適配者的子類,由于適配器和適配者之間是關(guān)聯(lián)關(guān)系,根據(jù)“里氏代換原則”,適配者的子類也可通過該適配器進(jìn)行適配。
4.2 主要缺點(diǎn)
? ??類適配器模式的缺點(diǎn)如下:
? ? (1)?對(duì)于Java、C#等不支持多重類繼承的語言,一次最多只能適配一個(gè)適配者類,不能同時(shí)適配多個(gè)適配者;
? ??對(duì)象適配器模式的缺點(diǎn)如下:
? ? 與類適配器模式相比,要在適配器中置換適配者類的某些方法比較麻煩。如果一定要置換掉適配者類的一個(gè)或多個(gè)方法,可以先做一個(gè)適配者類的子類,將適配者類的方法置換掉,然后再把適配者類的子類當(dāng)做真正的適配者進(jìn)行適配,實(shí)現(xiàn)過程較為復(fù)雜。
4.3 適配器模式具體應(yīng)用
? ? (1)復(fù)用已經(jīng)寫好的功能模塊,例如:已經(jīng)寫好了一個(gè)員工信息模塊,要重新開發(fā)一個(gè)新的企業(yè)管理系統(tǒng),可以復(fù)用員工信息模塊。
? ? (2)程序運(yùn)行時(shí)的日志信息,可以記錄到數(shù)據(jù)庫,也可以記錄到Txt文件中。現(xiàn)在變更需求,使日志信息能夠同時(shí)記錄到數(shù)據(jù)庫和Txt文件中。可以添加一個(gè)適配器類,適配數(shù)據(jù)庫操作和文件操作。
? ? (3)在軟件開發(fā)過程中,我們會(huì)開發(fā)出一些功能性模塊。如排序算法模塊、查找模塊、或者封裝鏈表、隊(duì)列、樹等操作。在需要復(fù)用這些功能的時(shí)候,直接使用,無需重新開發(fā)這些功能模塊。
? ? (4)網(wǎng)上銀行加密模塊開發(fā): 已經(jīng)開發(fā)好了一套網(wǎng)銀加密模塊,現(xiàn)在需要重新開發(fā)一個(gè)類似的軟件,可以直接復(fù)用已經(jīng)開發(fā)好的加密模塊。
? ? (5)某公司已經(jīng)開發(fā)了一套火車票識(shí)別軟件,包含二值化、去噪、版面分析、欄目提取等模塊。現(xiàn)在要開發(fā)銀行卡識(shí)別項(xiàng)目,則可以直接復(fù)用火車票識(shí)別中的功能模塊。
? ? (6)某公司想要開發(fā)一套輸入法軟件,可以購買搜狗輸入法的搜索模塊,直接加以復(fù)用,無需自己重頭開發(fā)。
? ? (7)生活中的適配器: 電源適配器,USB轉(zhuǎn)串口線,水管專修工人使用各種接口連接本來沒法進(jìn)行相連的管道。
轉(zhuǎn)載于:https://blog.51cto.com/jiang890131/1889391
總結(jié)
以上是生活随笔為你收集整理的C++设计模式——适配器模式(对象适配器、类适配器)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UVa11427 Expect the
- 下一篇: linux黄金命令[积累中]