JAVA設計模式之模板方法模式和建造者模式

1、前期回顧

上一篇《Java 設計模式之工廠方法模式與抽象工廠模式》介紹了三種工廠模式,分別是工廠方法模式,簡單工廠方法模式,抽象工廠模式,文中詳細根據實際場景介紹了三種模式的定義,實踐,最後總結了三種方式的區別,以及各個模式的適用場景。這一篇博文咱們來學習下模板方法模式和建造者模式。java

2、模板方法模式和建造者模式的定義與實踐

2.1 模板方法模式的定義和實踐算法

定義:Define the skeleton of an algorithm in an operation.deferring some steps to subclasses.Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.設計模式

翻譯:定義一個操做中的算法框架,從而延遲子類中的一些步驟。使得子類能夠不改變算法結構的狀況下就能夠從新定義該算法的某些特定的步驟。框架

其實根據上面定義,咱們很快就能夠想到,這個模式的定義不就是繼承嗎?對,沒錯,模板方法模式最簡單的實現就是繼承。咱們先用代碼來實現一個模板方法模式。相信你們都吃過泡麪吧,咱們來用模板方法模式來泡個面吧。ide

public abstract class AbstractTemplate {
    /**
     * 燒開水*/
    public abstract void boilWater();
    /**煮麪條*/
    public abstract void cookNoodles();
    /**放調料*/
    public abstract void putCondiment();
    /**定義煮麪的模板,先燒水,再放麪條,最後放調料*/
    public void finish(){
        boilWater();
        cookNoodles();
        putCondiment();
        System.out.println("煮完啦,開吃咯!");
    }
}
/**煮方便麪模板實現類*/
public class InstantNoodlesTemplate extends AbstractTemplate{
    @Override
    public void boilWater() {
        System.out.println("燒開水啦!");
    }

    @Override
    public void cookNoodles() {
        System.out.println("放入方便麪啦!");
    }

    @Override
    public void putCondiment() {
        System.out.println("能夠放調料啦!");
    }
}
/**客戶端場景類*/
public class Client {
    public static void main(String[] args) {
        AbstractTemplate template=new InstantNoodlesTemplate();
        template.finish();
    }
}

上面的的finish()方法就是定義了一個算法框架,定義了煮麪條先要燒開水,而後放麪條,最後放調料的算法步驟。而後各個子類實現如何燒水,放什麼麪條,放什麼調料這樣的具體實現。這就是模板方法模式,僅僅經過繼承就實現了。就是這麼簡單,下面咱們來看看和模板方法模式相識的另一個模式,建造者模式。函數

2.21 建造者模式的定義和實踐post

定義:Separate the construction of a complex object from its representation so that the same construction process can create different representations.學習

翻譯:將一個複雜對象的構建與他的表現分離,使得一樣的構建過程能夠建立不一樣的表示。ui

這裏化重點,複雜對象的構建和表現分離,這裏用在上面煮麪的場景中就是說,煮麪的算法和定義是分離的,也就是說由各個具體的麪條品種決定如何去煮麪條,如何去放調料等等這些具體的算法步驟。咱們來用代碼實現下:this

//抽象煮苗條類
public abstract class AbstractNoodlesMode {
    private List<String> stepOrders = new ArrayList<>();

    /**
     * 燒開水
     */
    public abstract void boilWater();

    /**
     * 煮麪條
     */
    public abstract void cookNoodles();

    /**
     * 放調料
     */
    public abstract void putCondiment();

    /**
     * 煮麪條其餘步驟
     */
    public abstract void doOther();
    /**
     * 根據傳入的工序進行加工煮麪
     */
    final public void finish() {
        for (String stepOrder : this.stepOrders) {
            switch (stepOrder) {
                case "boilWater":
                    boilWater();
                    break;
                case "cookNoodles":
                    cookNoodles();
                    break;
                case "putCondiment":
                    putCondiment();
                    break;
                case "doOther":
                    doOther();
                    break;
                default:
                    System.out.println("沒法識別的烹飪指令");
                    break;
            }
        }
    }

    final public void setStepOrders(List<String> stepOrders) {
        this.stepOrders = stepOrders;
    }
}
/**方便麪的實現類,因爲只須要燒水,煮麪,放調料就好了,other方法就爲空*/
public class InstantNoodles extends AbstractNoodlesMode {
    @Override
    public void boilWater() {
        System.out.println("煮開水");
    }

    @Override
    public void cookNoodles() {
        System.out.println("放入方便麪");
    }

    @Override
    public void putCondiment() {
        System.out.println("放入調料");
    }

    @Override
    public void doOther() {

    }
}
/**意大利麪條實現類*/
public class Spaghetti extends AbstractNoodlesMode {
    @Override
    public void boilWater() {
        System.out.println("煮開水");
    }

    @Override
    public void cookNoodles() {
        System.out.println("放入意大利麪");
    }

    @Override
    public void putCondiment() {
        System.out.println("放入番茄醬");
    }

    @Override
    public void doOther() {
        System.out.println("放入火腿,早餐肉");
    }
}
/**抽象建造者類*/
public abstract class AbstractBuilder {
    /**定義煮麪條工序*/
    public abstract AbstractBuilder cookNoodles();
    /**完成麪條*/
    public abstract AbstractNoodlesMode build();
}
/**泡麪建造者實現類*/
public class InstantNoodlesBuilder extends AbstractBuilder {
    private AbstractNoodlesMode noodles=new InstantNoodles();
    @Override
    public AbstractBuilder cookNoodles() {
        List<String> steps=new ArrayList<>();
        //燒水
        steps.add("boilWater");
        //放麪條
        steps.add("cookNoodles");
        //放調料
        steps.add("putCondiment");
        this.noodles.setStepOrders(steps);
        return this;
    }

    @Override
    public AbstractNoodlesMode build() {
        return this.noodles;
    }

}

/*意大利麪條建造者實現類**/
public class SpaghettiBuilder extends AbstractBuilder {
    private AbstractNoodlesMode noodle = new Spaghetti();

    @Override
    public AbstractBuilder cookNoodles() {
        List<String> steps = new ArrayList<>();
        //燒水
        steps.add("boilWater");
        //放麪條
        steps.add("cookNoodles");
        //放調料
        steps.add("putCondiment");
        //放火腿,放早餐肉
        steps.add("doOther");
        this.noodle.setStepOrders(steps);
        return this;
    }

    @Override
    public AbstractNoodlesMode build() {
        return this.noodle;
    }
}

/**客戶端場景類*/
public class Client {
    public static void main(String[] args) {
        AbstractBuilder instantNoodleBuilder = new InstantNoodlesBuilder();
        AbstractBuilder spaghettiBuilder = new SpaghettiBuilder();
        AbstractNoodlesMode instantNoodle = instantNoodleBuilder.cookNoodles().build();
        instantNoodle.finish();
        System.out.println("--------------------");
        AbstractNoodlesMode spaghe = spaghettiBuilder.cookNoodles().build();
        spaghe.finish();
    }
}

咱們運行下結果以下:

上述代碼咱們整理成一個類圖以下:

上面類圖我省去了麪條實現類,可是這也不影響整個建造者模式的理解。這裏有同窗確定會問,這個建造者模式雖然比模板方法模式複雜點,可是感受他們很類似啊,有點弄混的感受啊。對,沒錯,他們確實很相識。可是咱們只要記住一點就能很好的區分他們。

  • 模板方法模式已經定義好了建造者的算法,也就是工序,先作什麼後作什麼。
  • 建造者模式沒有定義建造者的工序,而是交給子類去實現建造者的算法,也就是工序。

咱們只要記住這一點就能很好的區分模板方法模式和建造者模式,同時也就知道了何時採用模板方法模式,何時採用建造者模式。

3、模板方法模式和建造者模式的應用場景

3.1 模板方法模式的使用場景

1.多個子類有公有的方法,而且邏輯基本同樣時。

2.有重要核心的複雜算法,並且須要不少的複用性時,能夠把該算法用模板方法模式實現。

3.代碼重構升級時,模板方法模式是很經常使用的模式,通常爲了保證向下兼容,因此採用模板方法模式,在模板方法內會設置鉤子函數,根據不一樣的版本和不一樣的狀況執行不一樣的算法。

3.2 建造者模式的使用場景

1.相同的方法,不一樣的執行順序,產生不一樣的事件結果時,能夠採用建造者模式

2.多個部件或零件,均可以裝配到一個對象中,可是產生的結果又不相同。

4、模板方法模式和建造者模式的優勢與缺點

4.1模板方法模式

優勢:

  • 良好的擴展性,子類只要實現指定的算法便可,不用關注後續組合的算法
  • 良好的可維護性。
  • 符合依賴倒置原則

缺點:

  • 我的理解的缺點可能就是封裝死了核心組合方法,可是這就是模板方法的特色,姑且算一個缺點吧。

4.2建造者模式

優勢:

  • 和模板方法模式同樣具備良好的擴展性和可維護性
  • 靈活性,因爲連最核心的組合算法都交給子類去實現,因此更靈活。

缺點:

  • 風險性,因爲放開了組合算法,下降了約束性。因此可能會致使子類調用定義錯了算法,給系統帶來潛在的風險。

5、總結

模板方法模式和建造者模式都是屬於創造性模式,兩個模式代碼很類似,很容易弄混,咱們只要記住他們的核心區別就能夠知道何時該用模板方法模式,何時該用建造者模式。這裏再說一遍他們的主要區別: 模板方法模式定義了核心算法,也就是組裝工序,而建造者模式沒有定義建造的順序和構造多少零件,最後的核心工序以及使用零件實現徹底由子類去決定。

詳細細心的讀者確定以及發現了個人建造模式中的實現出現了instantNoodleBuilder.cookNoodles().build()這樣A.B.C的代碼,以前在咱們的設計模式開篇咱們談到了迪米特法則,迪米特法則有舉例說不要出現A.B.C的狀況,這裏說明下迪米特法則的要求實際上是不要出現依賴非朋友類的狀況,其實其實是不要出現A.getB.getC的狀況,而這裏其實每次返回的都是this,因此都是依賴的本身,並無出現非朋友類,因此這樣的寫法沒有違法迪米特法則。

6、參考

《設計模式之禪》

7、推薦閱讀

Java設計模式之工廠方法模式與抽象工廠模式

Java設計模式之單例模式

JAVA設計模式之開篇

帶你走進java集合之ArrayList

帶你走進java集合之HashMap

Java鎖之ReentrantLock(一)

Java鎖之ReentrantLock(二)

Java鎖之ReentrantReadWriteLock

相關文章
相關標籤/搜索