1. 程式人生 > >Head First 設計模式學習——簡單工廠方法-工廠方法模式-抽象工廠模式

Head First 設計模式學習——簡單工廠方法-工廠方法模式-抽象工廠模式

設計模式是進階高階開發的必經之路。
工廠方法模式(Factory Method Pattern)是《Head First 設計模式》介紹的第四個模式,只要你認真讀完本篇博文,一定能很好地掌握此模式。

定義

工廠方法模式通過讓子類決定該建立的物件是什麼,來達到將物件建立的過程封裝的目的

分析

程式要執行,需要建立各種物件。而我們之前為了鬆耦合、高可用做的努力,最後在main方法裡,都需要例項化具體的類物件才行,比如:

Noodles noodle= new BeefNoodles();//例項化牛肉麵
Beverage beverage = new MilkyTea();//原味奶茶

我們之前提倡的原則:面向介面程式設計,不面向實現程式設計。可一旦new 具體物件,程式立馬有了瑕疵。假設需要例項化多種子類物件,呼叫方法裡就要寫各種new,呼叫類裡就需要引入所有依賴的子類:

import BeefNoodles;//引入所有以來的實現型別
import DuckLegNoodles;
public order(){
    Noodles noodle = createNoodle("牛肉麵");
    noodle.cost();//計算價格
    noodle.send();//上菜
}
public createNoodle(面型別){
    Noodles noodle;
    //寫在客戶端程式碼裡的例項化
if(牛肉麵){ noodle = new BeefNoodles();//牛肉麵一份 }else (鴨腿面){ noodle = new DuckLegNoodles();//鴨腿面一份 } } noodle.doSomething();

若是以後需要的物件型別發生變化,此段程式碼必定要頻繁修改,造成隱患。根據第一原則:把變化的部分抽取、封裝。客戶端的邏輯可以簡化為:獲取物件——物件做些事情。我們把獲取物件的邏輯交給一個專門負責此類事情的高手去做:簡單工廠SimpleFactory。

老將出馬,一個頂倆

public class
SimpleFactory{ public Noodles getNoodles(引數){//可以為static方法 //建立物件的邏輯讓SimpleFactory具體負責。 Noodles noodle; if(牛肉麵){ noodle = new BeefNoodles();//牛肉麵一份 }else (鴨腿面){ noodle = new DuckLegNoodles();//鴨腿面一份 } return noodle; } } //客戶端呼叫 Noodles noodle = SimpleFactory.getNoodles(引數);

目前來看,我們只是把程式碼換了個位置,然後從客戶端呼叫,並沒有特別出彩的設計。不過有一點:客戶端已經不需要依賴各種具體實現了,因為不管什麼面都是面(超型別),客戶端獲得引用直接呼叫即可。但是,如果現在新增一個店也需要賣麵條。按照原來的方式,它需要寫一段例項化麵條的程式碼,並具有同樣的風險。現在,有了老司機:SimpleFactory,它只需將它的需求告訴簡單工廠,自然就能獲得自己想要的物件。這樣,以後不管多個客戶端,都可以複用簡單工廠。
這種把建立物件的邏輯封裝起來並提供簡單的對外介面,稱為簡單工廠模式(其實算不上一個模式,畢竟實在很簡單)。

客戶太多,老將扛不住

快樂的時光總是短暫的。

麵條太好吃,大家想開加盟店,開到北京上海,天津廣州雲南去。中國幅員遼闊,各地的口味不盡相同,為了適應當地風情,需要多款麵條產品,蘭州牛肉拉麵,北京牛肉擀麵,天津牛肉炒麵。。。(有沒有餓),大家向SimpleFactory請求拉麵,傳入品種標識,SimpleFactory裡的getNoodles()方法不斷增長:

    public Noodles getNoodles(引數){//可以為static方法
        //建立物件的邏輯讓SimpleFactory具體負責。
        Noodles noodle;
        if(牛肉麵){
            noodle = new BeefNoodles();//牛肉麵一份
        }else if(鴨腿面){
            noodle = new DuckLegNoodles();//鴨腿面一份
        }else if(){}else if(){}else if(){}else if(){}else if(){}else if(){}else if(){}else if(){}else if(){}....
        return noodle;
    }

這肯定不是個好辦法,畢竟一個工廠產能有限,要開發生產太多品類,也不利用積累,分散了精力。只能多開幾個工廠,把加盟店劃分區域,一個區域一個工廠,負責此區域內加盟店的麵條需求。

//客戶端呼叫
Noodles noodle = BeijingFactory.getNoodles(引數);//北京工廠
Noodles noodle = LanzhouFactory.getNoodles(引數);//蘭州工廠
...

本著抽象、封裝的原則,我們可以把SimpleFactory抽象成介面,因為工廠都是負責做麵條而已。只不過不同工廠做的步驟、原料不一樣(實現不同,但上層並不關心)

public interface NoodlesFactory{
    Noodles getNoodles(引數);
}

class BeijingFactory implements NoodlesFactory{
    public Noodles getNoodles(引數){
        Noodles noodle;
        //根據引數,專門生產北京地區風味的麵條
        return noodle;
    }
}

class LanzhouFactory implements NoodlesFactory{
    public Noodles getNoodles(引數){
        Noodles noodle;
        //根據引數,專門生產蘭州地區麵條
        return noodle;
    }
}

//客戶端:
持有一個麵條工廠引用:
class BeijingStory{
    NoodlesFactory noodlesFactory;
    public BeijingStory(){
        noodlesFactory = new BeijingFactory();
    }
    //下單
    public order(){
        //執行時才能知道是哪個工廠來生產自己需要的麵條
        Noodles noodle = noodlesFactory.getNoodles("牛肉麵");
        noodle.cost();//計算價格
        noodle.send();//上菜
    }
}

蘭州、廣州店同樣如此,找一個能做本地口味麵條的工廠(實現NoodlesFactory介面),就能繼續用訂單系統,不用改動太多。此時把簡單工廠升級,變成了工廠方法模式。可以看到,工廠方法模式是在簡單工廠上的進一步抽象、封裝。讓下層有更大、更多的實現自由,提高設計彈性,保留複用(工廠可以供應好幾個門店)。

升級——抽象工廠模式

簡單工廠方法——>工廠方法模式——>抽象工廠模式 是一套頂尖功法,不用自宮,也可成功。

本著誠信經營、真材實料的原則,所有門店生意都很好。但世界永遠在變,不可能永遠只賣麵條,別人想吃黃燜雞、瓦罐煨湯等國家著名連鎖小吃的時候,就不來店裡去別家了。為了解決這個問題,門店需要增加商品種類。按照之前的工廠方法模式,我們可以新增黃燜雞工廠YellowChickFactory和瓦罐煨湯工廠CrockSoupFactory:

public interface YellowChickFactory{
    YellowChick getYellowChick(String size);
}
class BeijingYellowChickFactory{
    public YellowChick getYellowChick(String size){
        //生產北京地區的黃燜雞,大份、中份、小份等
        return new BeijingYellowChick(size);
    }
}
//CrockSoupFactory 程式碼略...

這樣設計,遵循了我們之前的原則,帶來一定的好處。不過也有不足之處:畢竟麵條不是時時刻刻都需要,在有黃燜雞訂單時,黃燜雞工廠生產,意味著麵條工廠就處停工狀態(綜合來說是這樣,畢竟總客戶資源某種程度上看是固定的),而麵條工廠開工,則黃燜雞工廠停工。

兩個工廠的生產效率極大浪費。此時我們想到,能不能把連個工廠結合起來,即能生產麵條又能做黃燜雞,甚至瓦罐湯?

當然可以,我們可以抽象一個總的工廠介面即抽象工廠FoodFactory,裡面有生產麵條、黃燜雞、瓦罐湯和其他商品的介面。各地區的工廠只需要實現此介面,實現自己的生產流程,就能滿足所有的需求了:

public interface FoodFactory{
    Noodles getNoodles(引數);
    YellowChick getYellowChick(String size);
    //瓦罐湯和其他類似,略
}

class BeijingFactory implements FoodFactory{
   public Noodles getNoodles(引數){
        Noodles noodle;
        ///...
        //根據引數,專門生產北京地區麵條
        return noodle;
    }
    public YellowChick getYellowChick(String size){
        //生產北京地區的黃燜雞,大份、中份、小份等
        return new BeijingYellowChick(size);
    }
}
//蘭州、廣州的工廠同樣如此,只不過具體的實現邏輯不通

門店呼叫:

//北京門店:
持有一個抽象工廠引用:
class BeijingStory{
    FoodFactory foodFactory;
    public BeijingStory(){
        foodFactory= new BeijingFactory();
    }
    //麵條下單
    public noodlesOrder(){
        Noodles noodle = foodFactory.getNoodles("牛肉麵");
        ...
    }
    //黃燜雞下單
    public noodlesOrder(){
        YellowChick chick= getYellowChick.getNoodles("小份");
        ...
    }
}

此時:我們在工廠方法模式的基礎上,合併多個工廠,並抽象出一個抽象工廠。此抽象工廠包含了一組生產物件的方法,子類實現此介面,來滿足系統的呼叫。此模式就是:抽象工廠模式