前言
今天Carson來全面總結最常用的設計模式 - 適配器模式。
其他設計模式介紹
這是一份全面 & 詳細的設計模式學習指南
Carson帶你學設計模式:單例模式(Singleton)
Carson帶你學設計模式:簡單工廠模式(SimpleFactoryPattern)
Carson帶你學設計模式:工廠方法模式(Factory Method)
Carson帶你學設計模式:抽象工廠模式(Abstract Factory)
Carson帶你學設計模式:策略模式(Strategy Pattern)
Carson帶你學設計模式:適配器模式(Adapter Pattern)
Carson帶你學設計模式:靜態代理模式(Proxy Pattern)
Carson帶你學設計模式:動態代理模式(Proxy Pattern)
Carson帶你學設計模式:模板方法模式(Template Method)
Carson帶你學設計模式:建造者模式(Builder Pattern)
Carson帶你學設計模式:外觀模式(Facade Pattern)
Carson帶你學設計模式:觀察者模式(Observer)
目錄
1. 介紹
1.1 模式說明
定義一個包裝類,用于包裝不兼容接口的對象
- 包裝類 = 適配器Adapter;
- 被包裝對象 = 適配者Adaptee = 被適配的類
1.2 主要作用
把一個類的接口變換成客戶端所期待的另一種接口,從而使原本接口不匹配而無法一起工作的兩個類能夠在一起工作。
適配器模式的形式分為:類的適配器模式 & 對象的適配器模式
1.3 解決的問題
原本由于接口不兼容而不能一起工作的那些類可以在一起工作
2. 模式原理
2.1 類的適配器模式
類的適配器模式是把適配的類的API轉換成為目標類的API。
2.1.1 UML類圖 & 組成
在上圖中可以看出:
- 沖突:Target期待調用Request方法,而Adaptee并沒有(這就是所謂的不兼容了)。
- 解決方案:為使Target能夠使用Adaptee類里的SpecificRequest方法,故提供一個中間環節Adapter類(繼承Adaptee & 實現Target接口),把Adaptee的API與Target的API銜接起來(適配)。
Adapter與Adaptee是繼承關系,這決定了這個適配器模式是類的
2.1.2 使用步驟(代碼解析)
步驟1: 創建Target接口;
public interface Target {
//這是源類Adapteee沒有的方法
public void Request();
}
步驟2: 創建源類(Adaptee) ;
public class Adaptee {
public void SpecificRequest(){
}
}
步驟3: 創建適配器類(Adapter)
//適配器Adapter繼承自Adaptee,同時又實現了目標(Target)接口。
public class Adapter extends Adaptee implements Target {
//目標接口要求調用Request()這個方法名,但源類Adaptee沒有方法Request()
//因此適配器補充上這個方法名
//但實際上Request()只是調用源類Adaptee的SpecificRequest()方法的內容
//所以適配器只是將SpecificRequest()方法作了一層封裝,封裝成Target可以調用的Request()而已
@Override
public void Request() {
this.SpecificRequest();
}
}
步驟4:定義具體使用目標類,并通過Adapter類調用所需要的方法從而實現目標
public class AdapterPattern {
public static void main(String[] args){
Target mAdapter = new Adapter();
mAdapter.Request();
}
}
2.1.3 實例講解
接下來我用一個實例來對類的適配器模式進行更深一步的介紹。
a. 實例概況
- 背景:小成買了一個進口的電視機
- 沖突:進口電視機要求電壓(110V)與國內插頭標準輸出電壓(220V)不兼容
- 解決方案:設置一個適配器將插頭輸出的220V轉變成110V
即適配器模式中的類的適配器模式
b. 使用步驟
步驟1: 創建Target接口(期待得到的插頭):能輸出110V(將220V轉換成110V)
public interface Target {
//將220V轉換輸出110V(原有插頭(Adaptee)沒有的)
public void Convert_110v();
}
步驟2: 創建源類(原有的插頭) ;
class PowerPort220V{
//原有插頭只能輸出220V
public void Output_220v(){
}
}
步驟3:創建適配器類(Adapter)
class Adapter220V extends PowerPort220V implements Target{
//期待的插頭要求調用Convert_110v(),但原有插頭沒有
//因此適配器補充上這個方法名
//但實際上Convert_110v()只是調用原有插頭的Output_220v()方法的內容
//所以適配器只是將Output_220v()作了一層封裝,封裝成Target可以調用的Convert_110v()而已
@Override
public void Convert_110v(){
this.Output_220v;
}
}
步驟4:定義具體使用目標類,并通過Adapter類調用所需要的方法從而實現目標(不需要通過原有插頭)
//進口機器類
class ImportedMachine {
@Override
public void Work() {
System.out.println("進口機器正常運行");
}
}
//通過Adapter類從而調用所需要的方法
public class AdapterPattern {
public static void main(String[] args){
Target mAdapter220V = new Adapter220V();
ImportedMachine mImportedMachine = new ImportedMachine();
//用戶拿著進口機器插上適配器(調用Convert_110v()方法)
//再將適配器插上原有插頭(Convert_110v()方法內部調用Output_220v()方法輸出220V)
//適配器只是個外殼,對外提供110V,但本質還是220V進行供電
mAdapter220V.Convert_110v();
mImportedMachine.Work();
}
}
2.2 對象的適配器模式
與類的適配器模式相同,對象的適配器模式也是把適配的類的API轉換成為目標類的API。
與類的適配器模式不同的是,對象的適配器模式不是使用繼承關系連接到Adaptee類,而是使用委派關系連接到Adaptee類。
2.2.1 UML類圖
在上圖中可以看出:
- 沖突:Target期待調用Request方法,而Adaptee并沒有(這就是所謂的不兼容了)。
- 解決方案:為使Target能夠使用Adaptee類里的SpecificRequest方法,故提供一個中間環節Adapter類(包裝了一個Adaptee的實例),把Adaptee的API與Target的API銜接起來(適配)。
Adapter與Adaptee是委派關系,這決定了適配器模式是對象的。
2.2.2 使用步驟(代碼解析)
步驟1: 創建Target接口;
public interface Target {
//這是源類Adapteee沒有的方法
public void Request();
}
步驟2: 創建源類(Adaptee) ;
public class Adaptee {
public void SpecificRequest(){
}
}
步驟3: 創建適配器類(Adapter)(不適用繼承而是委派)
class Adapter implements Target{
// 直接關聯被適配類
private Adaptee adaptee;
// 可以通過構造函數傳入具體需要適配的被適配類對象
public Adapter (Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void Request() {
// 這里是使用委托的方式完成特殊功能
this.adaptee.SpecificRequest();
}
}
步驟4:定義具體使用目標類,并通過Adapter類調用所需要的方法從而實現目標
public class AdapterPattern {
public static void main(String[] args){
//需要先創建一個被適配類的對象作為參數
Target mAdapter = new Adapter(new Adaptee());
mAdapter.Request();
}
}
在這里我就不再舉實例進行講解了(詳情請看上面“進口機器的插頭”),只是在適配類實現時將“繼承”改成“在內部委派Adaptee類”而已
3. 優缺點
3.1 適配器模式
優點
- 更好的復用性
系統需要使用現有的類,而此類的接口不符合系統的需要。那么通過適配器模式就可以讓這些功能得到更好的復用。 - 透明、簡單
客戶端可以調用同一接口,因而對客戶端來說是透明的。這樣做更簡單 & 更直接 - 更好的擴展性
在實現適配器功能的時候,可以調用自己開發的功能,從而自然地擴展系統的功能。 - 解耦性
將目標類和適配者類解耦,通過引入一個適配器類重用現有的適配者類,而無需修改原有代碼 - 符合開放-關閉原則
同一個適配器可以把適配者類和它的子類都適配到目標接口;可以為不同的目標接口實現不同的適配器,而不需要修改待適配類
缺點
- 過多的使用適配器,會讓系統非常零亂,不易整體進行把握
3.2 類的適配器模式
優點
- 使用方便,代碼簡化
僅僅引入一個對象,并不需要額外的字段來引用Adaptee實例
缺點
- 高耦合,靈活性低
使用對象繼承的方式,是靜態的定義方式
3.3 對象的適配器模式
優點
- 靈活性高、低耦合
采用 “對象組合”的方式,是動態組合方式
缺點
- 使用復雜
需要引入對象實例
特別是需要重新定義Adaptee行為時需要重新定義Adaptee的子類,并將適配器組合適配
4. 應用場景
4.1 適配器的使用場景
- 系統需要復用現有類,而該類的接口不符合系統的需求,可以使用適配器模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作
- 多個組件功能類似,但接口不統一且可能會經常切換時,可使用適配器模式,使得客戶端可以以統一的接口使用它們
4.2 類和對象適配器模式的使用場景
靈活使用時:選擇對象的適配器模式
類適配器使用對象繼承的方式,是靜態的定義方式;而對象適配器使用對象組合的方式,是動態組合的方式。需要同時配源類和其子類:選擇對象的適配器
- 對于類適配器,由于適配器直接繼承了Adaptee,使得適配器不能和Adaptee的子類一起工作,因為繼承是靜態的關系,當適配器繼承了Adaptee后,就不可能再去處理 Adaptee的子類了;
- 對于對象適配器,一個適配器可以把多種不同的源適配到同一個目標。換言之,同一個適配器可以把源類和它的子類都適配到目標接口。因為對象適配器采用的是對象組合的關系,只要對象類型正確,是不是子類都無所謂。
- 需要重新定義Adaptee的部分行為:選擇類適配器
- 對于類適配器,適配器可以重定義Adaptee的部分行為,相當于子類覆蓋父類的部分實現方法。
- 對于對象適配器,要重定義Adaptee的行為比較困難,這種情況下,需要定義Adaptee的子類來實現重定義,然后讓適配器組合子類。雖然重定義Adaptee的行為比較困難,但是想要增加一些新的行為則方便的很,而且新增加的行為可同時適用于所有的源。
- 僅僅希望使用方便時:選擇類適配器
- 對于類適配器,僅僅引入了一個對象,并不需要額外的引用來間接得到Adaptee。
- 對于對象適配器,需要額外的引用來間接得到Adaptee。
總結
建議盡量使用對象的適配器模式,多用合成/聚合、少用繼承。
當然,具體問題具體分析,根據需要來選用合適的實現方式
5. 總結
- 本文主要對適配器模式進行了全面介紹;
- 接下來我會對每種設計模式進行詳細的分析,歡迎關注Carson_Ho的簡書,不定期分享關于安卓開發的干貨,追求短、平、快,但卻不缺深度。
請點贊!因為你的鼓勵是我寫作的最大動力!
相關文章閱讀
這是一份全面 & 詳細的設計模式學習指南
Carson帶你學設計模式:單例模式(Singleton)
Carson帶你學設計模式:簡單工廠模式(SimpleFactoryPattern)
Carson帶你學設計模式:工廠方法模式(Factory Method)
Carson帶你學設計模式:抽象工廠模式(Abstract Factory)
Carson帶你學設計模式:策略模式(Strategy Pattern)
Carson帶你學設計模式:適配器模式(Adapter Pattern)
Carson帶你學設計模式:靜態代理模式(Proxy Pattern)
Carson帶你學設計模式:動態代理模式(Proxy Pattern)
Carson帶你學設計模式:模板方法模式(Template Method)
Carson帶你學設計模式:建造者模式(Builder Pattern)
Carson帶你學設計模式:外觀模式(Facade Pattern)
Carson帶你學設計模式:觀察者模式(Observer)