一天一個設計模式(五) - 適配器模式(Adapter)
前言
適配器模式把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作。
適配器模式的用途
最經典的就是電器的例子,筆記本電腦的插頭一般都是三相的,即除了陽極、陰極之外,還有一個地極。而有些地方的電源插座卻只有兩極,沒有地極。電源插座與筆記本電腦的電源插頭不匹配使得筆記本電腦無法使用。這時候一個三相到兩相的轉換器(適配器)就能解決此問題,而這正像是本模式所做的事情。
適配器模式的形式
適配器模式有類的適配器模式和對象的適配器模式兩種不同的形式。
正文
類的適配器模式
類的適配器模式,簡單來說,就是適配的類的API
轉換成為目標接口的API
。
從上圖可以看出,Adaptee
類並沒有sampleOperation2()
方法,而客戶端則期待這個方法。
為了使客戶端能夠使用Adaptee
類,提供一個中間環節,即類Adapter
,把Adaptee
類的API
同Target
接口的API
銜接起來。Adapter
與Adaptee
是繼承關系,這決定了這個適配器模式是類的適配器模式。
相關角色
- 目標(Target)角色:這就是所期待得到的接口。註意:由於這裏討論的是類適配器模式,因此目標不可以是類。
- 源(Adaptee)角色:現在需要適配的到目標角色的類。
- 適配器(Adapter)角色:適配器是目標角色和源角色之間的橋梁。適配器把源角色的類轉換成目標接口
示例代碼
Target.java
1
|
public interface Target {
|
上面給出的是目標角色的接口代碼,這個角色是以一個接口的形式實現的。可以看出,這個接口聲明了兩個方法:sampleOperation1()
和sampleOperation2()
Adaptee
是一個具體類,它有一個sampleOperation1()
方法,但是沒有sampleOperation2()
方法。
Adaptee.java
1
|
public class Adaptee {
|
適配器角色Adapter
拓展了Adaptee
,同時又實現了目標角色Target
接口。由於Adaptee
沒有提供sampleOperation2()
方法,而目標接口有要求這個方法,因此適配器角色Adapter
實現了這個方法。
Adapter.java
1
|
public class Adapter extends Adaptee implements Target {
|
對象的適配器模式
與類的適配器模式一樣,對象的適配器模式把被適配類的API
轉換成為目標類的API
。
與類的適配器模式不同的是,對象的適配器模式不是使用繼承關系鏈接到Adaptee
類,而是使用委派關系連接到Adaptee
類。
從上圖可以看出,Adaptee
類並沒有sampleOperation2()
方法,而客戶端則期待這個方法。
為使客戶端能夠使用Adaptee
類,需要提供一個包裝Wrapper
類Adapter
。這個包裝類包括了一個Adaptee
的實例,從而此包裝類能夠把Adaptee
的API
與Target
類的API
銜接起來。Adapter
類與Adaptee
類是委派關系,這決定了適配器模式是對象的。
相關角色
- 目標(Target)角色:這就是所期待得到的接口。註意:由於這裏討論的是類適配器模式,因此目標不可以是類。
- 源(Adaptee)角色:現在需要適配的到目標角色的類。
- 適配器(Adapter)角色:適配器是目標角色和源角色之間的橋梁。適配器把源角色的類包裝到目標接口的實現中。
示例代碼
Target.java
1
|
public interface Target {
|
上面給出的是目標角色的接口代碼,這個角色是以一個接口的形式實現的。可以看出,這個接口聲明了兩個方法:sampleOperation1()
和sampleOperation2()
,而源角色Adaptee
是一個具體類,它有一個sampleOperation1()
方法,但是沒有sampleOperation2()
方法。
Adaptee.java
1
|
public class Adaptee {
|
在對象的適配器模式中,適配器角色中持有一個對源角色的引用,並在需要適配的方法中使用源角色的方法實現。
Adapter.java
1
|
public class Adapter {
|
兩種適配器模式的對比
類的適配器模式
- 使用對象繼承的方式,是靜態的定義方式。
- 由於適配器直接繼承了
Adaptee
,使得適配器不能和Adaptee
的子類一起工作。因為繼承是靜態的關系,而適配器繼承了Adaptee
後,就不可能再去處理Adaptee
的子類了。 - 適配器可以重定義
Adaptee
的部分行為,相當於子類覆蓋父類的部分實現方法。 - 不需要額外的引用過來間接得到
Adaptee
。
對象的適配器模式
- 使用對象組合的方式,是動態的組合方式。
- 一個適配器可以把多種不同的適配源適配到同一個目標類上。換言之,同一個適配器可以把源類和它的子類都適配到目標接口。因為對象適配器采用的是對象組合的關系,只要對象類型正確,是不是子類都無所謂。
- 要重定義
Adaptee
的行為比較困難,這種情況下,需要定義Adaptee
的子類來實現重定義,然後讓適配器組合子類。這樣,雖然增加了一定的復雜性,也提供了一定的靈活性。 - 需要額外的引用來間接得到
Adaptee
。
建議盡量使用對象適配器的實現方式,多用合成/聚合,少用繼承。當然,具體問題還是需要具體分析,根據需要來選用實現方式,最適合的才是最好的。
總結
適配器模式的優點
- 更好的復用性
系統需要使用現有的類,因此類的接口不符合系統的需要。那麽通過適配器模式就可以讓這些功能得到更好的復用。
- 更好的拓展性
在實現適配器功能的時候,可以調用自己開發的功能,從而自然的拓展系統的功能。
適配器模式的缺點
過多的使用適配器,會讓系統非常零亂,不易整體進行把握。比如,明明看到調用的是A
接口,其實內部都被適配成了B
接口的實現。一個系統如果太多的出現這種情況,無異於異常災難。
因此如果不是很有必要,可以不是用適配器,而是直接對系統進行重構。
歡迎關註技術公眾號: 零壹技術棧
本帳號將持續分享後端技術幹貨,包括虛擬機基礎,多線程編程,高性能框架,異步、緩存和消息中間件,分布式和微服務,架構學習和進階等學習資料和文章。
一天一個設計模式(五) - 適配器模式(Adapter)