Java設計模式之從[滑鼠介面][星際爭霸中的兵種行為]分析介面卡(Adapter)模式

Froser發表於2014-04-07

  介面卡將一個類的介面轉換成客戶希望的另外一個介面。下面用三個例子來反映介面卡的不同用途:

  情況一:

  我買了一個PS2介面的滑鼠,但是我的電腦沒有PS2的介面,僅有USB的介面。為了不浪費這個滑鼠,我跑到商店買了一個PS2到USB的轉接頭,這樣我就用上了PS2的滑鼠。USB轉接頭就是一個典型的介面卡。

interface USBPort {
    void connect();  
}  
  
interface PS2Port { 
    void connect();  
}  
  
class USBMouse implements USBPort {
    public void connect(){  
        System.out.println("滑鼠連線上了USB埠!");  
    }  
}  

class PS2Mouse implements PS2Port{
    public void connect(){  
        System.out.println("滑鼠連線上了PS2埠!");  
    }
}
  
class PS22USBMouseAdapter implements USBPort{
    private USBPort usbMouse;  
    public PS22USBMouseAdapter (PS2Port ps2Port){
        System.out.println("正在將PS2介面轉換為USB介面...");
        //在此處我們僅僅是新建了一個USBMouse物件,對於更加複雜的情況,我們可以對PS2Port的物件進行很多操作
        usbMouse = new USBMouse();
    }  
    public void connect(){  
        usbMouse.connect();  
    }  
}  
  
class Adapter1  
{  
    public static void main(String[] args) {  
        PS2Mouse mouse = new PS2Mouse();  
        mouse.connect();  
        USBPort adapter = new PS22USBMouseAdapter(mouse);  
        adapter.connect();  
    }  
} 


  PS22USBMouseAdapter是一個PS2到USB介面的介面卡,由於它最終輸出的是USB資訊,因此繼承了USBPort。我們最後例項化adapter時,它的型別是USBPort —— 設計模式就是如此,我們一般用物件的抽象來進行宣告和定義,而不是具體的類。PS22USBMouseAdapter實現了PS2介面向USB介面的轉變。

  情況二:

  星際爭霸中,人族的頂級兵種為戰鬥巡洋艦(Battlecruiser)。巡洋艦上有個技能叫做大和炮(Yamato gun)。

  假設我們在設計巡洋艦的時候,並沒有想到它可以裝載大和炮,那麼我們的巡洋艦類(Battlecruiser)中僅有一個attack方法。後來我們希望給巡洋艦增加一個大和炮的技能,但是我們又不想修改巡洋艦類的程式碼,這個時候就可以用介面卡來實現,此時的介面卡可以理解成,我們在巡洋艦上裝一個轉換裝置,使得它可以安裝和發射大和炮。

class Battlecruiser{
    public void attack(){
        System.out.println("大和戰艦發起進攻!");
    }
}

class YamatoAdapter extends Battlecruiser implements Yamato {
    public void yamato(){
        System.out.println("大和戰艦使用大和炮進行攻擊!");
    }
}

interface Yamato{
    void attack();
    void yamato();
}

class Adapter2
{
    public static void main(String[] args) {
        Yamato superBattlecruiser = new YamatoAdapter(); 
        superBattlecruiser.attack();
        superBattlecruiser.yamato();
    }
}

  注意,Battlecruiser類是我們一開始就設計好的。為了能給巡洋艦裝上大和炮,我們新建了一個Yamato介面,這個介面有原來巡洋艦的attack方法,並且還增加了一個yamato方法。最後,建立一個繼承於巡洋艦的類YamatoAdapter,在測試類中用Yamato例項化一個巡洋艦,它既可以普通攻擊,又可以用大和炮進行攻擊。

  你可能會想,明明YamatoAdapter已經繼承了Battlecruiser,為何還要繼承Yamato介面?原因如之前所說,我們用“介面”來定義物件,而不是用具體的類。

  情況三:

  我需要設計星際爭霸人族兵種的一些抽象過程。如,所有兵種都要繼承於一個介面,這個介面有attack、stop、move等方法。這也就意味著,任何一個兵種類都要實現attack、stop、move這三個方法。

  這樣一來會有一個問題:並非所有的兵種都有attack、stop、move這3個行為。例如人族的醫療兵(Medic),她不具有attack行為(遊戲中她是無法攻擊敵人的);再例如人族的禿鷹戰車(Vulture,我們俗稱的佈雷車),它可以埋蜘蛛雷。蜘蛛雷在敵人靠近的時候,它才會進行自爆攻擊。也就是說,蜘蛛雷不具有stop和move方法,只具有attack方法。

  可見,一旦介面的方法多了起來,我們為了實現它們得手工寫不少程式碼,要是其中若干方法是我們該類不會用到的,我們不得不寫一個空方法。為了減少程式碼編寫,這時,我們可以使用介面的介面卡模式:

interface StarcraftSoldier{
    void attack();
    void stop();
    void move();
}

abstract class SoldierAdapter implements StarcraftSoldier {
    public void attack(){}
    public void stop(){}
    public void move(){}
}

class SpiderMine extends SoldierAdapter {
    public void attack(){
        System.out.println("蜘蛛雷發現敵人,進攻!");
    }
}

class Adapter3
{
    public static void main(String[] args) {
        new SpiderMine().attack();
    }
}

  介面的介面卡SoldierAdapter為介面StarcraftSoldier的所有方法做了空實現,在我們建立SpiderMine時,繼承SoldierAdapter,就不必將介面中的所有方法都實現一次了。

  以上就是我所理解的介面卡的用途,我把它理解成一個相容裝置,或者轉換裝置。若有牽強的地方,歡迎大家指出。

相關文章