設計模式——介面卡模式 Java原始碼
前言
《Head First Design Patterns》給的程式碼的例子是關於鴨子和火雞,然而鴨子和火雞離日常生活比較遠。這次,我改編了實驗樓網站上面的例子,關於插座和充電器。
圖:不同國家的插座,插頭不一樣,呵呵噠
圖:所以需要寫一個介面卡模式
圖:我繪製的介面卡類圖
情景:美國的插座,提供110伏電壓;中國的插座,提供220伏電壓。
- 在中國,用兩孔插座充電
- 然後坐飛機去美國旅遊,假設美國某旅館的牆上有隻有一個三孔插座
- 幸好我有美國介面卡,一頭插到三孔插座,另一頭轉換成二孔插座,就可以給我的榮耀手機充電
- 在美國,通過美國介面卡,用三空插座充電
總共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版)》中的例子就是這樣的。