Android 架構(gòu)師6 設(shè)計(jì)模式之模板方法模式

前言

模板方法模式就是定義一個操作中的算法框架,而將一些步驟延遲到子類中,使得子類不改變算法的結(jié)構(gòu)即可重復(fù)定義算法的某些特點(diǎn)步驟。

模板方法模式.png

需求

近日,樂視創(chuàng)始人賈躍亭造FF汽車的消息被廣而告之。假如你是制造商,賈躍亭讓你去制造一個車模型,以便讓其觀看并修改。

基本實(shí)現(xiàn)

定義一個車模抽象類CarModel ,里面有車模基本的方法:

public abstract class CarModel {
    //車模啟動
    public abstract void start();
    //車模停止
    public abstract void stop();
    //車模引擎奏響
    public abstract void engineBoom();
    //車模跑動過程中會按喇叭
    public abstract void alarm();
    //車模跑動
    public abstract void run();
}

FF汽車模型類CarFFModel :

public class CarFFModel extends CarModel {
    @Override
    public void start() {
        System.out.println("FF汽車模型啟動...");
    }

    @Override
    public void stop() {
        System.out.println("FF汽車模型停止...");
    }

    @Override
    public void engineBoom() {
        System.out.println("FF汽車模型引擎發(fā)出聲音...");
    }

    @Override
    public void alarm() {
        System.out.println("FF汽車模型跑動中奏鳴喇叭...");
    }

    @Override
    public void run() {
        this.start();
        this.engineBoom();
        this.alarm();
        this.stop();
    }
}

再定義一個客戶端Client,在這里也就是賈躍亭咯:

public class Client {
    public static void main(String[] args) {
        CarModel ffmodel = new CarFFModel();
        ffmodel.run();
    }
}

運(yùn)行輸出為:

FF汽車模型啟動...
FF汽車模型引擎發(fā)出聲音...
FF汽車模型跑動中奏鳴喇叭...
FF汽車模型停止...

其實(shí)run方法的實(shí)現(xiàn)應(yīng)該放在父類上,因?yàn)槊總€車模型都可以有上述方法,故改動如下:
CarModel :

public abstract class CarModel {
    //車模啟動
    public abstract void start();
    //車模停止
    public abstract void stop();
    //車模引擎奏響
    public abstract void engineBoom();
    //車模跑動過程中會按喇叭
    public abstract void alarm();
    //車模跑動
    public void run() {
        this.start();
        this.engineBoom();
        this.alarm();
        this.stop();
    }
}

CarFFModel :

public class CarFFModel extends CarModel {
    @Override
    public void start() {
        System.out.println("FF汽車模型啟動...");
    }

    @Override
    public void stop() {
        System.out.println("FF汽車模型停止...");
    }

    @Override
    public void engineBoom() {
        System.out.println("FF汽車模型引擎發(fā)出聲音...");
    }

    @Override
    public void alarm() {
        System.out.println("FF汽車模型跑動中奏鳴喇叭...");
    }
}

模板方法模式

仔細(xì)看上面的寫法,發(fā)現(xiàn)必然有不妥的地方,比如客戶只關(guān)心在run的過程中,能夠聽到或看到這些就行,并不關(guān)心具體的實(shí)現(xiàn),只有子類需要關(guān)心,所以應(yīng)該將public改成protected,run方法既然子類都不能修改,是不是可以設(shè)置成final的呢?是滴是滴!
CarModel :

public abstract class CarModel {
    //車模啟動
    protected abstract void start();
    //車模停止
    protected abstract void stop();
    //車模引擎奏響
    protected abstract void engineBoom();
    //車模跑動過程中會按喇叭
    protected abstract void alarm();
    //車模跑動
    protected final void run() {
        this.start();
        this.engineBoom();
        this.alarm();
        this.stop();
    }
}

CarFFModel :

public class CarFFModel extends CarModel {
    @Override
    protected void start() {
        System.out.println("FF汽車模型啟動...");
    }

    @Override
    protected void stop() {
        System.out.println("FF汽車模型停止...");
    }

    @Override
    protected void engineBoom() {
        System.out.println("FF汽車模型引擎發(fā)出聲音...");
    }

    @Override
    protected void alarm() {
        System.out.println("FF汽車模型跑動中奏鳴喇叭...");
    }
}

大家看這個run方法,它定義了調(diào)用其他方法的順序,并且子類是不能修改的,這個就叫做模板方法。這樣模板方法設(shè)計(jì)模式就已經(jīng)完成了。

拓展

在上述方法中,這四個方法start、stop、engineBoom、alarm是子類必須實(shí)現(xiàn)的,叫做基本方法,基本方法有三種:在抽象類中實(shí)現(xiàn)了的叫做具體方法;在抽象類中沒有實(shí)現(xiàn),卻在子類中實(shí)現(xiàn)了的叫做抽象方法;還有一種叫做鉤子方法,待會會提到。

這時候,雖然模板方法模式已經(jīng)將車模造出來了,但是這時候賈躍亭又提了一個需求,要求建造另一個車模FFX,這個車模是不允許發(fā)出聲音的。那既然賈老板有要求,我們必須做啊:

CarModel 中定義了一個isAlaram鉤子方法,默認(rèn)是返回true:

public abstract class CarModel {
    //車模啟動
    protected abstract void start();
    //車模停止
    protected abstract void stop();
    //車模引擎奏響
    protected abstract void engineBoom();
    //車模跑動過程中會按喇叭
    protected abstract void alarm();
    //鉤子方法,默認(rèn)是能夠按喇叭的
    protected boolean isAlarm() {
        return true;
    }
    //車模跑動
    protected final void run() {
        this.start();
        this.engineBoom();
        if (this.isAlarm()) {
            this.alarm();
        }
        this.stop();
    }
}

CarFFXModel :

public class CarFFXModel extends CarModel {
    @Override
    protected void start() {
        System.out.println("FFX汽車模型啟動...");
    }

    @Override
    protected void stop() {
        System.out.println("FFX汽車模型停止...");
    }

    @Override
    protected void engineBoom() {
        System.out.println("FFX汽車模型引擎發(fā)出聲音...");
    }

    @Override
    protected void alarm() {
        System.out.println("FFX汽車模型跑動中奏鳴喇叭...");
    }

    @Override
    protected boolean isAlarm() {
        return false;
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        CarModel ffmodel = new CarFFXModel();
        ffmodel.run();
    }
}

運(yùn)行輸出結(jié)果為:

FFX汽車模型啟動...
FFX汽車模型引擎發(fā)出聲音...
FFX汽車模型停止...

這時候賈老板又不樂意了,他覺得喇叭應(yīng)該由客戶去決定,而不是車子決定,于是乎,我們又得做出修改:
CarModel :

public abstract class CarModel {
    private boolean isAlarm = true; //是否要響喇叭

    //車模啟動
    protected abstract void start();
    //車模停止
    protected abstract void stop();
    //車模引擎奏響
    protected abstract void engineBoom();
    //車模跑動過程中會按喇叭
    protected abstract void alarm();
    //鉤子方法,默認(rèn)是能夠按喇叭的
    protected boolean isAlarm() {
        return this.isAlarm;
    }
    //由客戶去決定
    public void setAlarm(boolean isAlarm) {
        this.isAlarm = isAlarm;
    }
    //車模跑動
    protected final void run() {
        this.start();
        this.engineBoom();
        if (this.isAlarm()) {
            this.alarm();
        }
        this.stop();
    }
}

CarFFXModel :

public class CarFFXModel extends CarModel {
    @Override
    protected void start() {
        System.out.println("FFX汽車模型啟動...");
    }

    @Override
    protected void stop() {
        System.out.println("FFX汽車模型停止...");
    }

    @Override
    protected void engineBoom() {
        System.out.println("FFX汽車模型引擎發(fā)出聲音...");
    }

    @Override
    protected void alarm() {
        System.out.println("FFX汽車模型跑動中奏鳴喇叭...");
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        CarModel ffmodel = new CarFFXModel();
        ffmodel.setAlarm(true);
        ffmodel.run();
    }
}

運(yùn)行輸出結(jié)果為:

FFX汽車模型啟動...
FFX汽車模型引擎發(fā)出聲音...
FFX汽車模型跑動中奏鳴喇叭...
FFX汽車模型停止...

要想使喇叭無效,只需要setAlarm(false)就可以,鉤子的作用就是這樣的。

模板方法模式在Android中的應(yīng)用

Activity、Fragment等其實(shí)就是利用了模板方法模式,我們寫的BaseActivity其實(shí)也一定程度上使用了模板方法模式,還有AsyncTask也用到了模板方法模式。

總結(jié)

模板方法模式就是在模板方法中按照一個的規(guī)則和順序調(diào)用基本方法,具體到我們上面那個例子就是 run 方法按照規(guī)定的順序(先調(diào)用 start,然后再調(diào)用 engineBoom,再調(diào)用alarm,最后調(diào)用 stop)調(diào)用本類的其他方法。

喜歡本篇博客的簡友們,就請來一波點(diǎn)贊,您的每一次關(guān)注,將成為我前進(jìn)的動力,謝謝!作者:zhang_pan

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容