一、什么是適配器模式
適配器這個詞我們應該很熟悉,天天都在使用,手機充電時,電源線頭頭就叫電源適配器,干什么用的呢?把220V電壓轉換為手機充電時使用的電壓,那適配器模式是不是很好理解了,下面看一下定義。
適配器模式(Adapter),將一個類的接口轉換成客戶希望的另外一個接口。使原本由於接口不兼容而不能一起工作的那些類可以一起工作。
適配器模式有“類適配器”和“對象適配器”兩種不同的形式。
1. 類適配器
通過繼承進行適配(類間繼承)。UML結構圖如下:
(1) Target
Target目標角色,該角色定義把其他類轉換為何種接口,也就是期望接口,通常情況下是一個接口或一個抽象類,一般不會是實現類。
1 public interface Target { 2 3 public void request(); 4 5 }
(2) Adaptee
Adaptee源角色,想把誰轉換為目標角色,這個“誰”就是源角色,它是已經存在的、運行良好的類或對象。
1 public class Adaptee { 2 3 public void specificRequest() { 4 System.out.println("特殊請求"); 5 } 6 7 }
(3) Adapter
Adapter適配器角色,是適配器模式的核心角色,它的職責是通過繼承或是類關聯的方式把源角色轉換為目標角色。
1 public class Adapter extends Adaptee implements Target { 2 3 @Override 4 public void request() { 5 super.specificRequest(); 6 } 7 8 }
(4) ConcreteTarget
目標角色的實現類。
1 public class ConcreteTarget implements Target { 2 3 @Override 4 public void request() { 5 System.out.println("普通請求"); 6 } 7 8 }
(5) Client
1 public class Client { 2 3 public static void main(String[] args) { 4 //原有業務邏輯 5 Target target = new ConcreteTarget(); 6 target.request(); 7 8 //增加適配器后的業務邏輯 9 Target target2 = new Adapter(); 10 target2.request(); 11 } 12 13 }
此時原有業務邏輯輸出“普通請求”,增加適配器后的業務邏輯輸出“特殊請求”,下同。
2. 對象適配器
通過對象層次的關聯關系進行委托(對象的合成關系/關聯關系)。UML結構圖如下:
(1) Target
客戶所期待的接口。目標可以是具體的或抽象的類,也可以是接口。
1 public class Target { 2 3 public void request() { 4 System.out.println("普通請求"); 5 } 6 7 }
(2) Adaptee
需要適配的類。
1 public class Adaptee { 2 3 public void specificRequest() { 4 System.out.println("特殊請求"); 5 } 6 7 }
(3) Adapter
通過在內部包裝一個Adaptee對象,把源接口轉換成目標接口。
1 public class Adapter extends Target { 2 3 private Adaptee adaptee = new Adaptee(); 4 5 @Override 6 public void request() { 7 adaptee.specificRequest(); 8 } 9 }
(4) Client
1 public class Client { 2 3 public static void main(String[] args) { 4 Target target = new Adapter(); 5 target.request(); 6 } 7 8 }
二、適配器模式的應用
1. 何時使用
- 系統需要使用現有的類,而此類的接口不符合系統的需要。
- 想建立一個可以重復使用的類,用於一些彼此之間沒有太大關聯的一些類。
- 通過接口轉換,將一個類插入另一個類系中。
2. 方法
- 繼承或依賴。
3. 優點
- 可以讓任何兩個沒有關聯的類一起運行。
- 增加了類的透明性。我們訪問Target目標角色,但具體實現都委托給了源角色,而這些對高層模塊是透明的,也是不需要關心的。
- 提高了類的復用度。源角色在原有的系統中還是可以正常使用,而在目標角色中也可以充當新的演員。
- 靈活性非常好。什么時候不想要適配器了,直接刪掉就可以了,基本上就類似一個靈活的構件,想用就用,不想用就卸載。
4. 缺點
- 過多使用適配器,會使系統非常零亂。
- 由於Java至多繼承一個類,所以至多只能適配一個適配者類,而且目標類必須是抽象類。
5. 使用場景
- 有動機地修改一個正常運行的系統的接口。
6. 應用實例
- 電源適配器。
- 在Linux上運行Windows程序。
- Java中的JDBC。
- 翻譯官。
7. 注意事項
- 只有碰到無法改變原有設計和代碼的情況下,才考慮適配器模式。
三、適配器模式的實現
下面我們以翻譯官為例,姚明剛去美國時,不懂英文,專門為他配備了翻譯,特別是在比賽場上,教練、隊員與他的對話全部都通過翻譯來溝通,這里翻譯就是適配器。現在編寫一個適配器模式的例子,火箭隊比賽,教練叫暫停時給后衛、中鋒、前鋒分配進攻和防守任務。UML圖如下:
1. Player類
抽象球員類,有進攻和防守兩種方法。
1 public abstract class Player { 2 3 protected String name; 4 5 public Player(String name) { 6 this.name = name; 7 } 8 9 public abstract void attack(); //進攻 10 public abstract void defense(); //防守 11 }
2. 前鋒、中鋒、后衛類
這里以前鋒為例,其余的就不過多贅述了。重寫了Player類中的進攻和防守兩個方法,此時是英文的,尚未翻譯。
1 public class Forwards extends Player { 2 3 public Forwards(String name) { 4 super(name); 5 } 6 7 @Override 8 public void attack() { 9 System.out.println("Forward " + name + " attack"); 10 } 11 12 @Override 13 public void defense() { 14 System.out.println("Forward " + name + " defense"); 15 } 16 17 }
3. ForeignCenters類
public class ForeignCenters { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void attackChi() { System.out.println("中鋒 " + name + " 進攻"); } public void defenseChi() { System.out.println("中鋒 " + name + " 防守"); } }
4. Translator類
翻譯者類,也就是適配器。對英文的進攻和防守進行翻譯。
1 public class Translator extends Player { 2 3 private ForeignCenters foreignCenter = new ForeignCenters(); 4 5 public Translator(String name) { 6 super(name); 7 foreignCenter.setName(name); 8 } 9 10 @Override 11 public void attack() { 12 foreignCenter.attackChi(); 13 } 14 15 @Override 16 public void defense() { 17 foreignCenter.defenseChi(); 18 } 19 20 }
5. Client
1 public class Client { 2 3 public static void main(String[] args) { 4 Player bPlayer = new Forwards("巴蒂爾"); 5 bPlayer.attack(); 6 7 Player mPlayer = new Guards("麥克格雷迪"); 8 mPlayer.attack(); 9 10 Player yPlayer = new Translator("姚明"); 11 yPlayer.attack(); 12 yPlayer.defense(); 13 } 14 15 }
運行結果如下:
從運行結果可以看到,“attack”和“defense”命令在姚明這里被翻譯成了“進攻”和“防守”。
四、類適配器和對象適配器的區別
從上面的內容可以看出來,類適配器是類間繼承,對象適配器是對象的合成關系,也可以說是類的關聯關系,這是兩者的根本區別。
由於對象適配器是通過類間的關聯關系進行耦合的,因此在設計時就可以做到比較靈活,而類適配器就只能通過覆寫源角色的方法進行擴展。
在實際項目中,對象適配器使用到的場景較多。