設計模式:用實際案例講解工廠模式

赫連小伍發表於2021-07-13

工廠模式有啥用啊,我的專案沒使用工廠模式也照樣執行

這是我聽過最令人哭笑不得的吐槽,這個程式猿的頭髮不知道有沒有被自己薅禿

的確,專案中不使用工廠模式並不會影響專案的執行

但是,當專案後期需要二次開發時,程式碼的維護和修改的複雜度,絕對能讓你恨不得把自己頭髮都薅禿

下面我們就來盤一盤工廠模式能解決哪些問題

簡單工廠模式

實際案例

假如客戶有這樣一個需求,做一個使用者訂購手機來玩遊戲的專案

專案中可以生產華為和小米的手機,生產的手機只能用來玩遊戲,使用者可以通過京東和淘寶來訂購手機

需求中的一個前置條件是手機只能用來玩遊戲,不能做別的事情。這就類似於一個規範,所有的手機都要遵守這個規範。

制定規範是java中介面的特性,所以我們要定義一個介面基類,叫做Phone,裡面有一個玩遊戲的方法play()

還要有華為手機和小米手機的類,分別叫做HuweiXiaomi,這兩個手機類要遵循手機只能用來玩遊戲這個規範,所以它們要實現Phone類,並完成play()方法

用java程式碼實現分別如下

phone基類

public interface Phone {
    void play();
}

華為手機類Huawei

public class Huawei implements Phone {
    @Override
    public void play() {
        System.out.println("華為手機玩遊戲");
    }
}

小米手機類Xiaomi

public class Xiaomi implements Phone {
    @Override
    public void play() {
        System.out.println("小米手機玩遊戲");
    }
}

使用者可以通過京東和淘寶來訂購手機,所以還要有京東和淘寶的類,分別叫做JingdongTaobao。類裡面各有一個訂購方法叫做order(),可以根據使用者的喜好來訂購不同的手機

如果使用者喜歡華為則訂購華為手機來玩遊戲,如果使用者喜歡小米則訂購小米手機來玩遊戲,其他的使用者就不能玩遊戲

因為京東類和淘寶類的程式碼邏輯一摸一樣,這裡只貼一下京東類的程式碼

public class Jingdong {
    public void order(String type) {
        Phone phone = null;
        if ("huawei".equals(type)) {
            phone = new Huawei();
            phone.play();
        } else if ("xiaomi".equals(type)) {
            phone = new Xiaomi();
            phone.play();
        } else {
            System.out.println("暫不支援");
        }
    }
}

這樣我們就實現了客戶的需求,而且沒有使用任何設計模式。

專案雖然可以完美的執行,但是有一個問題值得我們思考,假如又增加了蘋果手機,這時候我們的程式碼該怎麼修改

首先,我們肯定是要增加蘋果手機類IPhone,並且實現Phone基類

京東類中訂購方法的邏輯需要做出相應的修改

淘寶類中訂購方法的邏輯也需要做出相應的修改,修改的地方和京東類一模一樣。這時我們就發現同樣的程式碼需要修改兩次。

如果訂購類有很多,除了京東、淘寶,還有拼多多、微信商城等等。那就意味著同樣的程式碼不止要修改兩次,有多少個訂購類就需要修改多少次

這個工作量是很大的,而且極其容易出錯,就問你頭禿不頭禿

這時候就需要使用簡單工廠模式來優化我們的程式碼

簡單工廠模式就是建立一個工廠類,由這個類來封裝例項化物件的行為

套用到這個例子中就是:建立一個工廠類,由工廠類來封裝建立手機的邏輯。訂購類從工廠類中獲取手機物件直接使用,不再關心手機建立的過程

工廠類PhoneFactory用程式碼實現就是這樣

public class PhoneFactory {
    public static Phone createPhone(String type) {
        Phone phone = null;
        if ("huawei".equals(type)) {
            phone = new Huawei();
        } else if ("xiaomi".equals(type)) {
            phone = new Xiaomi();
        } else if ("apple".equals(type)) {
            phone = new IPhone();
        } else {
            System.out.println("暫不支援");
        }
        return phone;
    }
}

京東、淘寶等訂購類從工廠類裡面獲取手機物件後直接使用,不再關心手機的建立過程(京東和淘寶類的程式碼一樣,這裡只貼京東類的程式碼)

public class Jingdong {
    public void order(String type) {
        Phone phone = PhoneFactory.createPhone(type);
        if (phone != null) {
            phone.play();
        }
    }
}

這就實現了簡單工廠模式,以後再需要增加手機型號時只需要修改工廠類就行了,其他程式碼不用修改

簡單工廠模式總結

簡單工廠模式就是建立一個工廠類,根據傳入的引數型別來建立具體的產品物件,並返回產品物件的例項

主要適用於呼叫者不知道應該建立哪個具體的物件,只能根據傳入的條件返回相應物件的場景

比如案例中,訂購類是不知道要建立哪個手機物件的,只能根據使用者提供的條件才能知道需要哪個手機物件

簡單工廠模式的好處在於將物件的建立過程和使用過程進行解耦,減少新增具體產品時修改程式碼的複雜度

就像上面的例子一樣,物件的建立過程由工廠類負責,訂購類不需要關心物件是怎麼建立的,只需要從工廠類獲取物件來使用即可

當需要增加手機物件時,只需要修改工廠類,而不需要對每一個訂購類進行修改

簡單工廠模式的缺點在於每次新增具體產品時,都需要修改工廠類,這違背了設計模式中的開閉原則。而且當具體的產品比較多時,工廠類的if-else判斷就會比較多,程式碼不美觀

工廠方法模式

實際案例

基於剛才使用者訂購手機玩遊戲的需求,我們稍微改動一下。

為了實現精準營銷,京東、淘寶等商城分別上線了華為專賣店、小米專賣店和蘋果專賣店

當使用者進入華為專賣店,就預設使用者要訂購華為手機;當使用者進入小米專賣店,就預設使用者要訂購小米手機;當使用者進入蘋果專賣店,就預設使用者要訂購蘋果手機

這個需求用剛才我們講的簡單工廠模式也可以實現

但是簡單工廠的缺點也很明顯,當新增粉絲型別時需要修改工廠類,當粉絲型別過多時工廠類的邏輯就會比較繁雜

比如新增了vivo粉絲,工廠類就需要新增判斷條件去建立vivo手機物件;新增了oppo手機,工廠類就要新增判斷條件去建立oppo手機物件。一直不斷的新增下去的話,就會導致工廠類的中的判斷過多,程式碼很長,後期不容易維護

而且,簡單工廠模式是適用於呼叫者不知道應該建立哪種物件的場景。

在這個需求中,京東等訂購類中為不同的粉絲提供了專賣店,假如專賣店是訂購類中的一個方法的話,在專賣店這個方法中是知道應該建立什麼樣的物件的。比如,在華為手機的訂購方法中,是知道要建立華為手機物件的

所以,這個需求可以用工廠方法模式來實現

工廠方法模式和簡單工廠模式相似,也需要有一個工廠類。不過在工廠方法模式中,工廠類不再負責建立物件。因為在每個訂購方法中已經知道應該建立哪個手機物件,所以建立物件的邏輯下沉到訂購類的方法中

工廠類只負責制定規範,來約束每個產品的具體行為,所以工廠類是一個介面基類。每個對應的產品需要有一個自己的工廠,來繼承這個基類,達到約束自身行為的目的

這個需求中工廠基類規定每個工廠只能有一個方法,這個方法的作用就是建立手機物件

工廠基類PhoneFactory用程式碼實現

public interface PhoneFactory {
   Phone createPhone();
}

華為工廠類用程式碼實現

public class HuaweiFactory implements PhoneFactory {
   @Override
   public Phone createPhone() {
      return new Huawei();
   }
}

小米工廠類用程式碼實現

public class XiaomiFactory implements PhoneFactory {
   @Override
   public Phone createPhone() {
      return new Xiaomi();
   }
}

在訂購類中,不同的訂購方法呼叫不同的工廠獲取物件。比如京東訂購類的程式碼如下(淘寶訂購類處理邏輯類似,這裡不再貼淘寶類的程式碼)

public class Jingdong {
    // 華為粉絲訂購華為手機
    public void orderHuawei() {
        PhoneFactory phoneFactory = new HuaweiFactory();
        Phone phone = phoneFactory.createPhone();
        phone.play();
    }
    // 小米粉絲訂購小米手機
    public void orderXiaomi() {
        PhoneFactory phoneFactory = new XiaomiFactory();
        Phone phone = phoneFactory.createPhone();
        phone.play();
    }
}

這樣我們就用工廠方法模式實現了為不同粉絲訂購不同手機的需求

工廠方法模式總結

工廠方法模式是定義一個工廠介面基類,基類中定義一個建立產品的抽象方法。每個產品需要有自己的工廠來實現這個基類,並完成建立對應產品例項的方法,由具體的產品呼叫該方法來建立物件

它主要適用於呼叫者已經明確知道需要建立哪一個具體產品的場景

比如,針對華為的粉絲,已經明確知道要建立華為的手機產品

工廠方法模式的優勢在於完全符合了開閉原則,在新增產品時不需要再改動已存在的程式碼,使工廠類和產品類的程式碼完全解耦,更利於程式的擴充套件

他的缺點也很明顯,當新增產品時,需要同時新增產品類和工廠類,導致系統中的類是成對增加,增加了系統的複雜度

抽象工廠模式

實際案例

基於工廠方法模式的案例,我們再進一步擴充套件

使用者不單單想訂購手機來玩遊戲,還想訂購ipad和電腦

可以用剛才講的工廠方法模式來實現:我們不僅需要提供手機工廠的基類,還需要提供ipad工廠基類和電腦工廠基類,並且為每個工廠基類提供具體的工廠實現類

訂購類方法中,根據不同的需求來建立不同的產品供使用者使用

這樣實現的程式碼沒有問題,但是不符合我們真實開發中的業務場景

在實際業務場景中,京東商城的華為專賣店想要訂購手機不需要到華為公司的手機部門去訂購吧?想要訂購ipad不需要到華為公司的ipad部門訂購吧?想要訂購電腦也不需要到華為公司的電腦部門訂購吧?

京東商城的華為專賣店應該只負責和華為公司對接,和具體的業務部門沒關係。專賣店想要訂購某個產品去告訴華為公司,由公司去給具體的業務部門溝通

所以,從實際的使用場景出發,我們的程式碼應該這樣設計

不再使用單獨的手機工廠、ipad工廠和PC工廠,而是把同一個廠家作為工廠,由工廠分別建立不同的產品

Factory基類實現程式碼如下

public interface Factory {
    Phone createPhone();
    IPad createIPad();
    PC createPC();
}

華為工廠類實現程式碼如下

public class HuaweiFactory implements Factory {
    @Override
    public Phone createPhone() {
        return new HuaweiPhone();
    }
    @Override
    public IPad createIPad() {
        return new HuaweiIPad();
    }
    @Override
    public PC createPC() {
        return new HuaweiPC();
    }
}

小米工廠實現程式碼如下

public class XiaomiFactory implements Factory {
    @Override
    public Phone createPhone() {
        return new XiaomiPhone();
    }
    @Override
    public IPad createIPad() {
        return new XiaomiIPad();
    }
    @Override
    public PC createPC() {
        return new XiaomiPC();
    }
}

在京東訂購類中,我們只需要建立對應的工廠物件,由工廠物件建立不同的產品

public class Jingdong {
    // 華為粉絲訂購手機、ipad、電腦
    public void orderHuawei() {
        Factory factory = new HuaweiFactory();
        Phone phone = factory.createPhone();
        phone.play();
        IPad ipad = factory.createIPad();
        ipad.play();
        PC pc = factory.createPC();
        pc.play();
    }
    // 小米粉絲訂購手機、ipad、電腦
    public void orderXiaomi() {
        Factory factory = new XiaomiFactory();
        Phone phone = factory.createPhone();
        phone.play();
        IPad ipad = factory.createIPad();
        ipad.play();
        PC pc = factory.createPC();
        pc.play();
    }
}

這樣我們就用抽象工廠模式實現了使用者訂購手機、ipad和電腦的需求

抽象工廠模式總結

抽象工廠模式是將具有一定共性的產品封裝到一塊,由工廠類分別為這些產品提供建立物件的方法,呼叫者可以根據不同的需求呼叫工廠類的具體方法來獲得產品例項

比如案例中華為的手機、ipad和電腦都屬於華為公司產品,所以可以由華為工廠類來負責分別建立不同的物件

它的優勢在於將具有一定共性的產品集合封裝到一起,在實際開發中更符合具體的業務場景

他的缺點就是降低了系統的擴充套件性,當新增產品時需要修改工廠類,在工廠類的基類和實現類中都需要增加對應的方法

比如說,使用者也想訂購VR眼鏡來玩遊戲。那麼工廠基類中需要增加建立VR眼鏡的方法,所有的工廠實現類中都需要增加對該方法的實現,系統擴充套件性比較差

-- 文章來自赫連小伍公眾號

相關文章