設計模式——適配器模式 Java源代碼

前言

《Head First Design Patterns》給的代碼的例子是關於鴨子和火雞,然而鴨子和火雞離日常生活比較遠。這次,我改編了實驗樓網站上面的例子,關於插座和充電器。

充電插頭
圖:不同國家的插座,插頭不一樣,呵呵噠

適配器
圖:所以需要寫一個適配器模式

適配器類圖
圖:我繪製的適配器類圖

情景:美國的插座,提供110伏電壓;中國的插座,提供220伏電壓。

  1. 在中國,用兩孔插座充電
  2. 然後坐飛機去美國旅遊,假設美國某旅館的牆上有隻有一個三孔插座
  3. 幸好我有美國適配器,一頭插到三孔插座,另一頭轉換成二孔插座,就可以給我的榮耀手機充電
  4. 在美國,通過美國適配器,用三空插座充電

總共7個類
一個三孔插座接口(Adaptee, 被適配者)
一個三孔插座類
一個兩孔插座接口(Target, 適配目標)
一個兩孔插座類
一個適配器(Adapter:實現Target, 組合Adaptee
一個手機類(Client)
一個Main類,用於測試


talk is cheap, show me the code

三孔插座接口(Adaptee)

package adapter;

// adaptee(被適配者) ———— 假設在美國某旅館的牆上,只有一個三孔插座
public interface ThreePinSoket
{
    public void chargeWithThreePin();
    public int  voltage();
}

三孔插座類

package adapter;

// 實現一個具體的 adaptee
public class ThreePinSoketAmerica implements ThreePinSoket
{

    @Override
    public void chargeWithThreePin()
    {
        System.out.println("美國標準的三孔的插座");
    }

    @Override
    public int voltage()
    {
        return 110;      // 美國電壓是110伏 
    }

}

兩孔插座接口(Target)

package adapter;

// target(適配目標) ———— 我的榮耀手機充電器是兩個插頭,所以需要兩個插孔的插座
public interface TwoPinSoket
{
    public void   chargeWithTwoPin();
    public int    voltage();
}

兩孔插座類

package adapter;

// client(具體的adaptee) ———— 這個就是我在中國的牆上的兩個插孔的插座,我充電只能用這個
public class TwoPinSoketChina implements TwoPinSoket
{

    @Override
    public void chargeWithTwoPin()
    {
        System.out.println("中國標準的兩孔的插座");
    }

    @Override
    public int voltage()
    {
        return 220;      // 中國電壓是220伏
    }

}

適配器(Adapter)

實現Target, 組合Adaptee

package adapter;

// 去美國旅遊,必須帶上一個“美國適配器”:實現兩孔插座,組合三孔插座。用來給我的榮耀手機充電
public class AmericaAdapter implements TwoPinSoket // 實現兩孔插座(target)
{
    ThreePinSoket threePinSoket; // 組合三孔插座(adaptee)

    public AmericaAdapter(ThreePinSoket threePinSoket)
    {
        this.threePinSoket = threePinSoket;
    }

    @Override
    public void chargeWithTwoPin()
    {
        threePinSoket.chargeWithThreePin();
    }

    @Override
    public int voltage()
    {
        return threePinSoket.voltage() * 2; // 適配器把電壓從 110V 升到 220V 
    }

}

手機類(Client)

package adapter;

public class RongYao
{
    TwoPinSoket twoPinSoket;

    public RongYao() {}

    public void setTwoPinSoket(TwoPinSoket twoPinSoket)
    {
        this.twoPinSoket = twoPinSoket;
    }

    public void chargeRequest()
    {
        System.out.println("華爲榮耀手機, " + twoPinSoket.voltage() + " 伏特充電中\n");
    }

}

Main類,用於測試

package adapter;

public class Main
{

    public static void main(String[] args)
    {
        // 在中國,用兩孔插座充電
        TwoPinSoketChina twoPinSoketChina = new TwoPinSoketChina();
        RongYao myRongYao = new RongYao();
        myRongYao.setTwoPinSoket(twoPinSoketChina);
        myRongYao.chargeRequest();

        // 然後坐飛機去美國旅遊,美國某旅館的牆上有隻有一個三孔插座
        ThreePinSoketAmerica threePinSoketAmerica = new ThreePinSoketAmerica();
        testThreePin(threePinSoketAmerica);

        // 幸好我有美國適配器,一頭插到三孔插座,另一頭轉換成二孔插座,就可以給我的榮耀手機充電
        AmericaAdapter americaAdapter = new AmericaAdapter(threePinSoketAmerica);
        testTwoPin(americaAdapter);

        // 在美國,通過美國適配器,用三空插座充電
        myRongYao.setTwoPinSoket(americaAdapter);
        myRongYao.chargeRequest();

    }

    static void testTwoPin(TwoPinSoket twoPinSoket)
    {
        twoPinSoket.chargeWithTwoPin();
        System.out.println("電壓是" + twoPinSoket.voltage() + "伏特\n");
    }

    static void testThreePin(ThreePinSoket threePinSoket)
    {
        threePinSoket.chargeWithThreePin();
        System.out.println("電壓是" + threePinSoket.voltage() + "伏特\n");
    }
}

運行結果

直接從eclipse複製過來

華爲榮耀手機, 220 伏特充電中

美國標準的三孔的插座
電壓是110伏特

美國標準的三孔的插座
電壓是220伏特

華爲榮耀手機, 220 伏特充電中

分析

適配器模式有三個重要角色:

  • 目標角色(Target),要轉換成的目標接口。在我的代碼例子中,是中國的兩孔接口
  • 源角色(Adaptee),需要被轉換的源接口。在我的代碼例子中,是美國的三孔接口
  • 適配器角色(Adapter),核心是實現Target接口, 組合Adaptee接口

這樣,Adaptee和Target兩個原本不兼容的接口,就可以在一起工作了(我的榮耀手機就可以在美國充電了)。這裏的面向接口編程,得到了鬆耦合的效果。

美國的三孔插座可以實現Adaptee接口,那麼英國、法國的三孔插座也可以去實現Adaptee接口,它們都成爲了Adaptee接口的子類。在Adapter類中,由於組合了一個Adaptee的引用,根據Java的多態性,我就可以拿着相同的Adapter類去英國,法國充電了。

另一方面,Client類組合一個Target接口的引用。我們就可製造多個Adapter類,實現同一個Target接口。假設索尼手機的需要日本標準的兩孔插座,那麼寫一個日本兩孔插座類實現Target接口,我就可以拿着相同的Adapter類,在美國給日本的索尼手機充電了。

最後補充一點:《Head First Design Patterns》說到,適配器模式其實分爲兩種。一種是Object Adapter,另外一種是Class Adapter。本篇博客就是一個Object Adapter的例子。那麼Class Adapter是長什麼樣子的呢?它繼承Adaptee類,實現Target接口 。《設計模式 (Java版)》中的例子就是這樣的。

更多設計模式,在新標籤頁中打開這裏~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章