前言
《設(shè)計(jì)模式自習(xí)室》系列鸵赖,顧名思義毕贼,本系列文章帶你溫習(xí)常見(jiàn)的設(shè)計(jì)模式温赔。主要內(nèi)容有:
- 該模式的介紹,包括:
- 引子鬼癣、意圖(大白話(huà)解釋?zhuān)?/li>
- 類(lèi)圖陶贼、時(shí)序圖(理論規(guī)范)
- 該模式的代碼示例:熟悉該模式的代碼長(zhǎng)什么樣子
- 該模式的優(yōu)缺點(diǎn):模式不是萬(wàn)金油,不可以濫用模式
- 該模式的實(shí)際使用案例:了解它在哪些重要的源碼中被使用
該系列會(huì)逐步更新于我的博客和公眾號(hào)(博客見(jiàn)文章底部)待秃,也希望各位觀眾老爺能夠關(guān)注我的個(gè)人公眾號(hào):后端技術(shù)漫談拜秧,不會(huì)錯(cuò)過(guò)精彩好看的文章。
系列文章回顧
- 【設(shè)計(jì)模式自習(xí)室】開(kāi)篇:為什么我們要用設(shè)計(jì)模式章郁?
- 【設(shè)計(jì)模式自習(xí)室】建造者模式
- 【設(shè)計(jì)模式自習(xí)室】原型模式
- 【設(shè)計(jì)模式自習(xí)室】透徹理解單例模式
- 【設(shè)計(jì)模式自習(xí)室】理解工廠(chǎng)模式的三種形式
結(jié)構(gòu)型——適配器模式
引子
當(dāng)你從中國(guó)到國(guó)外旅游時(shí)枉氮,經(jīng)常會(huì)購(gòu)買(mǎi)一個(gè)電源適配器,也就是電源轉(zhuǎn)接頭暖庄,因?yàn)閲?guó)外的插座經(jīng)常無(wú)法適用于國(guó)內(nèi)的插頭嘲恍,這個(gè)電源轉(zhuǎn)接頭就是一個(gè)適配器,也就是下圖中的Adapter雄驹,它讓你的插頭能夠正常的在國(guó)外使用佃牛。
客戶(hù)類(lèi)調(diào)用適配器的方法時(shí)泣特,在適配器類(lèi)的內(nèi)部將調(diào)用適配者類(lèi)的方法带兜,而這個(gè)過(guò)程對(duì)客戶(hù)類(lèi)是透明的平项,客戶(hù)類(lèi)并不直接訪(fǎng)問(wèn)適配者類(lèi)逃顶。
定義
適配器模式把一個(gè)類(lèi)的接口變換成客戶(hù)端所期待的另一種接口薪捍,從而使原本因接口不匹配而無(wú)法在一起工作的兩個(gè)類(lèi)能夠在一起工作危融。
適配器模式有類(lèi)的適配器模式和對(duì)象的適配器模式兩種不同的形式陵刹。
1. 類(lèi)適配器
創(chuàng)建新類(lèi)啃洋,繼承源類(lèi)霞怀,同時(shí)還要實(shí)現(xiàn)新接口
class adapter extends oldClass implements newFunc{}
2. 對(duì)象適配器
創(chuàng)建新類(lèi)的實(shí)例惫东,其中包含舊的類(lèi),并實(shí)現(xiàn)新接口
class adapter implements newFunc { private oldClass oldInstance ;}
- 類(lèi)適配器使用對(duì)象繼承的方式毙石,是靜態(tài)的定義方式
- 對(duì)象適配器使用對(duì)象組合的方式廉沮,是動(dòng)態(tài)組合的方式。
類(lèi)圖
如果看不懂UML類(lèi)圖徐矩,可以先粗略瀏覽下該圖滞时,想深入了解的話(huà),可以繼續(xù)谷歌滤灯,深入學(xué)習(xí):
適配器模式包含如下角色:
- Target:目標(biāo)抽象類(lèi):這就是所期待得到的新接口類(lèi)坪稽。
- Adapter:適配器類(lèi):適配器類(lèi)是本模式的核心曼玩。適配器把源接口轉(zhuǎn)換成目標(biāo)接口。顯然窒百,這一角色不可以是接口黍判,而必須是具體類(lèi)。
- Adaptee:適配者類(lèi):等待適配的舊類(lèi)
- Client:客戶(hù)端調(diào)用者
1. 類(lèi)適配器
2. 對(duì)象適配器
時(shí)序圖
時(shí)序圖(Sequence Diagram)是顯示對(duì)象之間交互的圖篙梢,這些對(duì)象是按時(shí)間順序排列的样悟。時(shí)序圖中顯示的是參與交互的對(duì)象及其對(duì)象之間消息交互的順序。
我們可以大致瀏覽下時(shí)序圖庭猩,如果感興趣的小伙伴可以去深究一下:
代碼示例/使用場(chǎng)景舉例
這次我將代碼示例和使用場(chǎng)景兩個(gè)章節(jié)合起來(lái)講,是因?yàn)橛幸粋€(gè)十分典型的Java 源碼可以學(xué)習(xí)
1. Java容器中的Enumeration老接口和Iterator新接口
JDK1.1 之前提供的容器有Arrays,Vector,Stack,Hashtable,Properties,BitSet陈症,其中定義了一種訪(fǎng)問(wèn)群集內(nèi)各元素的標(biāo)準(zhǔn)方式蔼水,稱(chēng)為 Enumeration(列舉器)接口。
JDK1.2 版本中引入了 Iterator接口录肯,新版本的集合對(duì)(HashSet,HashMap,WeakHeahMap,ArrayList,TreeSet,TreeMap, LinkedList)是通過(guò) Iterator 接口訪(fǎng)問(wèn)集合元素趴腋。
這樣,如果將老版本的程序運(yùn)行在新的 Java 編譯器上就會(huì)出錯(cuò)论咏。因?yàn)?List 接口中已經(jīng)沒(méi)有 elements()优炬,而只有 iterator() 了。那么如何將老版本的程序運(yùn)行在新的 Java 編譯器上呢? 如果不加修改厅贪,是肯定不行的蠢护,但是修改要遵循“開(kāi)-閉”原則我們可以用 Java 設(shè)計(jì)模式中的適配器模式解決這個(gè)問(wèn)題。
public class EnumIter implements Iterator {//重點(diǎn)1:配器實(shí)現(xiàn)目標(biāo)接口
private Enumeration e;//重點(diǎn)2:適配器持有被適配接口(對(duì)象)的引用
public EnumIter(Enumeration e){
this.e = e;
}
//將對(duì)目標(biāo)對(duì)象的調(diào)用轉(zhuǎn)發(fā)給真正的被適配的對(duì)象
public boolean hasNext() {
return e.hasMoreElements();
}
public Object next() {
return e.nextElement();
}
public void remove() {
}
}
NewEnumeration 是一個(gè)適配器類(lèi)养涮,通過(guò)它實(shí)現(xiàn)了從 Iterator 接口到 Enumeration 接口的適配葵硕,這樣我們就可以使用老版本的代碼來(lái)使用新的集合對(duì)象了。
2. JDBC也是一種適配器模式
Sun公司在1996年公開(kāi)了Java語(yǔ)言的數(shù)據(jù)庫(kù)連接工具JDBC贯吓,JDBC使得Java語(yǔ)言程序能夠與數(shù)據(jù)庫(kù)連接懈凹,并使用SQL語(yǔ)言來(lái)查詢(xún)和操作數(shù)據(jù)。JDBC給出一個(gè)客戶(hù)端通用的抽象接口悄谐,每一個(gè)具體數(shù)據(jù)庫(kù)引擎(如SQL Server介评、Oracle、MySQL等)的JDBC驅(qū)動(dòng)軟件都是一個(gè)介于JDBC接口和數(shù)據(jù)庫(kù)引擎接口之間的適配器軟件爬舰。抽象的JDBC接口和各個(gè)數(shù)據(jù)庫(kù)引擎API之間都需要相應(yīng)的適配器軟件们陆,這就是為各個(gè)不同數(shù)據(jù)庫(kù)引擎準(zhǔn)備的驅(qū)動(dòng)程序。
模式優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 將目標(biāo)類(lèi)和適配者類(lèi)解耦情屹,而無(wú)須修改原有代碼棒掠。
- 對(duì)于客戶(hù)端類(lèi)來(lái)說(shuō)是透明的,而且提高了適配者的復(fù)用性屁商。
類(lèi)適配器模式還具有如下優(yōu)點(diǎn):
由于適配器類(lèi)是適配者類(lèi)的子類(lèi)烟很,因此可以在適配器類(lèi)中置換一些適配者的方法颈墅,使得適配器的靈活性更強(qiáng)。
對(duì)象適配器模式還具有如下優(yōu)點(diǎn):
一個(gè)對(duì)象適配器可以把多個(gè)不同的適配者適配到同一個(gè)目標(biāo)雾袱,也就是說(shuō)恤筛,同一個(gè)適配器可以把適配者類(lèi)和它的子類(lèi)都適配到目標(biāo)接口。
缺點(diǎn)
過(guò)多的使用適配器芹橡,會(huì)讓系統(tǒng)非常零亂毒坛,不易整體進(jìn)行把握。比如林说,明明看到調(diào)用的是A接口煎殷,其實(shí)內(nèi)部被適配成了B接口的實(shí)現(xiàn),一個(gè)系統(tǒng)如果太多出現(xiàn)這種情況腿箩,無(wú)異于一場(chǎng)災(zāi)難豪直。
因此如果不是很有必要,可以不使用適配器珠移,而是直接對(duì)系統(tǒng)進(jìn)行重構(gòu)弓乙。
總結(jié)
建議盡量使用對(duì)象適配器的實(shí)現(xiàn)方式,多用合成/聚合钧惧、少用繼承暇韧。當(dāng)然,具體問(wèn)題具體分析浓瞪,根據(jù)需要來(lái)選用實(shí)現(xiàn)方式懈玻,最適合的才是最好的。