1. 程式人生 > >JAVA設計模式什麼鬼(模板方法)——作者:凸凹裡歐

JAVA設計模式什麼鬼(模板方法)——作者:凸凹裡歐

面向物件,是對事物屬性與行為的封裝,方法,指的就是行為。模板方法,顯而易見是說某個方法充當了模板的作用,其充分利用了抽象類虛實結合的特性,虛部抽象預留,實部固定延續,以達到將某種固有行為延續至子類的目的。反觀介面,則達不到這種目的。要搞明白模板方法,首先我們從介面與抽象類的區別切入,這也是面試官經常會問到的問題。

汽車上的介面最常見的就是這幾個了,點菸器,USB,AUX等等,很明顯這些都是介面,它們都預留了某種標準,暴露在系統外部,並與外設對接。就拿點菸器介面來說吧,它原本是專門用於給點菸器供電的,後來由於這個介面在汽車上的通用性,於是衍生出了各種外部裝置,只要是符合這個標準size的,帶正負極簧片的,直流12V的,那就可以使用,比如導航、行車記錄儀、吸塵器什麼的,以及其他各種車載電子裝置。

public interface CigarLighterInterface {//點菸器介面
    //供電方法,16V直流電
    public void electrifyDC16V();
}
1 public class GPS implements CigarLighterInterface {
2    //導航的實現
3    @Override
4    public void electrifyDC16V() {
5        System.out.println("連線衛星");
6        System.out.println("定位。。。");
7    }
8
9 }
 1 public class CigarLighter implements CigarLighterInterface {
 2    //點菸器的實現
 3    @Override
 4    public void electrifyDC16V() {
 5        int time = 1000;
 6        while(--time>0){
 7            System.out.println("加熱電爐絲");
 8        }
 9        System.out.println("點菸器彈出");
10    }
11
12 }

對於點菸器介面來說,它根本不在乎也不知道對接的外設是什麼鬼,它只是定義了一種規範,一種標準,只要符合的都可以對接。再比如USB介面的應用更加廣泛,外設更是應有盡有,具體例子可以參考文章《設計模式是什麼鬼(初探)》。

以上我們可以體會到介面的抽象是淋漓盡致的,實現是空無的,也就是說其方法都是無實現的。然而這樣在某些場景下會存在一些問題,例如有時候我們在父類中只需抽象出一些方法,並且同時也有一些實體方法,以供子類直接繼承,這怎麼辦?答案就是抽象類。舉個例子,哺乳動物類,我們人類就是哺乳動物。

什麼?鯨魚是哺乳類?是的,凡是餵奶的都是哺乳類,不要以為會游泳的都是魚,會飛的都是鳥,蝙蝠同樣是哺乳類,只不過是老鼠中的飛行員而已。

 

既然如此這哺乳動物肯定是都能餵奶了,但是到底是跑還是遊,或是飛呢還真不好說,但至少可以確認它們都是可以移動的。言歸正傳,我們開始定義哺乳動物抽象類。

 1 public abstract class Mammal {
 2
 3    //既然是哺乳動物當然會餵奶了,但這裡約束為只能母的餵奶
 4    protected final void feedMilk(){
 5        if(female){//如果是母的……
 6            System.out.println("餵奶");
 7        }else{//如果是公的……或者可以拋個異常出去。
 8            System.out.println("公的不會");
 9        }
10    }
11
12    //哺乳動物當然可以移動,但具體怎麼移動還不知道。
13    public abstract void move();
14 }

這裡我們省略了female屬性,其作用是為了控制餵奶行為,大家可以自行新增。可以看到的是,抽象類不同於介面,其自身是可以有具體實現的,也就是說抽象類是虛實結合的,虛部抽象行為,實部遺傳給子類,虛虛實實,飄忽不定。OK,我們看下人、鯨、蝠的子類實現。

public class Human extends Mammal {

    @Override
    public void move() {
        System.out.println("兩條腿走路……");
    }

}
public class Whale extends Mammal {

    @Override
    public void move() {
        System.out.println("游泳……");
    }

}
public class Bat extends Mammal {

    @Override
    public void move() {
        System.out.println("用翅膀飛……");
    }

}

可以看到子類的各路實現都是自己獨有的行為方式,而餵奶那個行為是不需要自己實現的,它是屬於抽象哺乳類的共有行為,哺乳子類不能進行任何干涉。這便是介面與抽象的最大區別了,介面是虛的,抽象類可以有虛有實,介面不在乎實現類是什麼,抽象類會延續其基因給子類。

其實到這裡我們已經說了一大半了,理解了以上部分,剩下的部分就非常簡單了,利用抽象類的這個特性,便有了“模板方法”。舉例說明,我們做軟體專案管理,按瀑布式簡單來講分為:需求分析、設計、編碼、測試、釋出,先不管是用何種方式去實現各個細節,我們就抽象成這五個步驟。

public abstract class PM {
    protected abstract void analyze();//需求分析
    protected abstract void design();//設計
    protected abstract void develop();//開發
    protected abstract boolean test();//測試
    protected abstract void release();//釋出
}

那麼問題來了,有個程式設計師在需求不明確或者設計不完善的情況下,一上來二話不說直接寫程式碼,這樣就會造成資源的浪費,後期改動太大還會影響專案進度。那麼專案經理這時就應該規範一下這個任務流程,這裡我們加入kickoff()方法實現。

 1 public abstract class PM {
 2    protected abstract void analyze();//需求分析
 3    protected abstract void design();//設計
 4    protected abstract void develop();//開發
 5    protected abstract boolean test();//測試
 6    protected abstract void release();//釋出
 7
 8    protected final void kickoff(){
 9        analyze();
10        design();
11        develop();
12        test();
13        release();
14    }
15 }

這樣就限制了整個專案週期的任務流程,注意這裡要用final宣告此方法子類不可以重寫,只能乖乖的繼承下去用。至於其他的抽象方法,子類可以自由發揮,比如測試方法test(),子類可以用人工測試,自動化測試,我們不care,我們是站在專案管理的抽象高度,只把控流程進度。這裡甚至我們還可以加入一些邏輯如下。

 1 public abstract class PM {
 2    protected abstract void analyze();//需求分析
 3    protected abstract void design();//設計
 4    protected abstract void develop();//開發
 5    protected abstract boolean test();//測試
 6    protected abstract void release();//釋出
 7
 8    protected final void kickoff(){
 9        analyze();
10        design();
11        do {
12            develop();
13        } while (!test());//如果測試失敗,則繼續開發改Bug。
14        release();
15    }
16 }

以下子類只需實現抽象方法,而不用實現固有的模板方法kickoff(),因為它已經被父類PM實現了,並且子類也不能進行重寫。

1 public class AutoTestPM extends PM{
2
3    @Override
4    protected void analyze() {
5        System.out.println("進行業務溝通,需求分析");     
6    }
7
8    //design();develop();test();release();實現省略
9 }

至此,我們的模板方法就完成了,抽象類PM中的實方法kickoff()中,以某種邏輯編排呼叫了其他各個抽象方法,提供了一種固定模式的行為方式或是指導方針,以此達到虛實結合、柔中帶剛、剛柔並濟,靈活中不失規範的目的。

當然大部分情況我們使用介面會多於抽象類,因為介面靈活啊,抽象類不允許多繼承啊等等,其實我們還是要看應用場景,在某種無規矩不成方圓,或者規範比較明確,的情況下抽象類的應用是有必要的,世間萬物沒有最好的,只有最合適的。