簡說設計模式——適配器模式


一、什么是適配器模式

  適配器這個詞我們應該很熟悉,天天都在使用,手機充電時,電源線頭頭就叫電源適配器,干什么用的呢?把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”命令在姚明這里被翻譯成了“進攻”和“防守”。

四、類適配器和對象適配器的區別

       從上面的內容可以看出來,類適配器是類間繼承,對象適配器是對象的合成關系,也可以說是類的關聯關系,這是兩者的根本區別。

       由於對象適配器是通過類間的關聯關系進行耦合的,因此在設計時就可以做到比較靈活,而類適配器就只能通過覆寫源角色的方法進行擴展。

       在實際項目中,對象適配器使用到的場景較多。

 

       源碼地址:https://gitee.com/adamjiangwh/GoF


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM