寂然解讀設(shè)計(jì)模式 - 工廠模式 - 抽象工廠模式


I walk very slowly, but I never walk backwards 

工廠模式 - 抽象工廠模式


? 寂然

大家好~,我是寂然,本節(jié)課呢,我們接著來聊工廠模式的第三種,抽象工廠模式,以及工廠模式在 JDK源碼中的應(yīng)用,最后,我們明確幾點(diǎn)工廠模式的注意事項(xiàng),對工廠模式進(jìn)行總結(jié),那我們啟程吧

基本介紹 - 抽象工廠模式

抽象工廠模式:定義了一個 interface 用于創(chuàng)建相關(guān)或有依賴關(guān)系的對象簇,而無需指明具體的類

對象簇即一系列對象

抽象工廠模式可以將簡單工廠模式和工廠方法模式進(jìn)行整合

案例回顧

怎么理解上面的幾句話呢,首先,我們來回顧一下上節(jié)課的需求,然后我們通過類圖來詳細(xì)說明

有這樣一個披薩店的需求,披薩的種類很多(比如 GreekPizz、CheesePizz 等)

披薩的制作有 prepare,bake,cut,box 等,而且客戶在點(diǎn)披薩時,可以在不同城市點(diǎn)披薩, 比如北京點(diǎn)了芝士 pizza、水果 pizza 或者在上海點(diǎn)了芝士 pizza、水果 pizza等

要求:完成披薩店訂購功能,便于披薩種類的擴(kuò)展,便于維護(hù)

類圖演示

OK,需求明確了,我們根據(jù)需求,繪制類圖,分析抽象工廠模式的實(shí)現(xiàn)思路,類圖如下圖所示


在這里插入圖片描述

為什么說抽象工廠模式可以將簡單工廠模式和工廠方法模式進(jìn)行整合,首先仍然是簡單工廠的思想,由工廠類封裝實(shí)例化對象的行為,其次,createPizza()方法下沉到各個子類,又體現(xiàn)出工廠方法模式的特點(diǎn)

從設(shè)計(jì)層面看,抽象工廠模式就是對簡單工廠模式的改進(jìn),或者稱為進(jìn)一步的抽象,將工廠抽象成兩層,一個AbsFactory(抽象工廠)和 具體實(shí)現(xiàn)的工廠子類,程序員可以根據(jù)創(chuàng)建對象類型使用對應(yīng)的工廠子類,這樣將單個的簡單工廠類變成了工廠簇,更利于代碼的維護(hù)和擴(kuò)展

解決方案四:抽象工廠模式

根據(jù)上面類圖的思路,我們對披薩訂購項(xiàng)目再次進(jìn)行重構(gòu),使用抽象工廠模式來完成功能,示例代碼如下圖所示

//披薩抽象類
public abstract class Pizza {

    //定義一個屬性,披薩的名稱,并給定set方法
    protected String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    //認(rèn)為不同的披薩,準(zhǔn)備的原材料不同,所以定義成抽象方法
    public abstract void prepare();

    //烘焙方法
    public void bake() {

        System.out.println(name + "正在烘焙中");
    }

    //切割方法
    public void cut() {

        System.out.println(name + "正準(zhǔn)備把披薩大卸八塊");
    }

    //打包方法
    public void box() {

        System.out.println(name + "將披薩打包給顧客");
    }
}

//北京的水果披薩
public class BJFruitPizza extends Pizza {

    @Override
    public void prepare() {

        setName("北京的水果披薩");
        System.out.println("北京的水果披薩正在準(zhǔn)備原材料");
    }
}

//北京的希臘披薩
public class BJGreekPizza extends Pizza {

    @Override
    public void prepare() {

        setName("北京的希臘披薩");
        System.out.println("北京的希臘披薩正在準(zhǔn)備原材料");
    }
}

//上海的水果披薩
public class SHFruitPizza extends Pizza {

    @Override
    public void prepare() {

        setName("上海的水果披薩");
        System.out.println("上海的水果披薩正在準(zhǔn)備原材料");
    }
}

//上海的希臘披薩
public class SHGreekPizza extends Pizza {

    @Override
    public void prepare() {

        setName("上海的希臘披薩");
        System.out.println("上海的希臘披薩正在準(zhǔn)備原材料");
    }
}

//抽象工廠模式-  抽象接口
public interface AbsFactory {

    //讓下面的工廠子類來具體實(shí)現(xiàn)
    public Pizza createPizza(String orderType);

}

//北京工廠子類
public class BJFactory implements AbsFactory{

    @Override
    public Pizza createPizza(String orderType) {

        Pizza pizza = null;

        if (orderType.equals("GreekPizza")){

            pizza = new BJGreekPizza();

        }else if (orderType.equals("FruitPizza")){

            pizza = new BJFruitPizza();
        }

        return pizza;
    }
}

//上海工廠子類
public class SHFactory implements AbsFactory{

    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;

        if (orderType.equals("GreekPizza")){

            pizza = new SHGreekPizza();

        }else if (orderType.equals("FruitPizza")){

            pizza = new SHFruitPizza();
        }

        return pizza;
    }
}

//訂購Pizza類
public class OrderPizza {

    //聚合抽象工廠接口
    AbsFactory absFactory;

    public OrderPizza(AbsFactory absFactory){

        setAbsFactory(absFactory);

    }

    public void setAbsFactory(AbsFactory absFactory) {

        Pizza pizza = null;

        String orderType = ""; //用戶輸入

        this.absFactory = absFactory;

        while (true){

            orderType = getType();

            pizza = absFactory.createPizza(orderType);

            if (pizza != null){  //訂購成功,輸出制作過程

                pizza.prepare();

                pizza.bake();

                pizza.cut();

                pizza.box();

            } else{

                System.out.println("訂購失敗,咱這沒有,去別處把");

                break;
            }
        }
    }

    //定義方法獲取客戶希望訂購的披薩種類
    private String getType(){

        System.out.println("你想訂購那個種類的Pizza呢?");

        Scanner scanner = new Scanner(System.in);

        String str = scanner.next();

        return str;
    }
}

//客戶端演示
public class PizzaStore {

    public static void main(String[] args) {

        System.out.println("請輸入要訂購披薩的地點(diǎn)");

        String address = new Scanner(System.in).next();

        if (address.equals("北京")){

            new OrderPizza(new BJFactory());

        }else if (address.equals("上海")){

            new OrderPizza(new SHFactory());

        }else{

            System.out.println("該地點(diǎn)暫未開通訂購披薩功能");

        }
    }
}

案例解讀

上面我們使用抽象工廠模式進(jìn)行了代碼重構(gòu),其實(shí)抽象工廠模式代碼層面并不難,重點(diǎn)在于分析過程和類圖的關(guān)系要清晰,其實(shí)這兩種方式差別是有的,但是不大,核心思想大同小異,抽象工廠模式只不過把工廠抽象成了兩層,而簡單工廠模式就是一個工廠類,但是大多數(shù)情況下,一個工廠類很難滿足復(fù)雜業(yè)務(wù)邏輯下多種不同類型的實(shí)例的創(chuàng)建,這個時候,使用抽象工廠模式搭建,會更顯得得心應(yīng)手,所以,雖然工廠模式有三種,但是大家要根據(jù)實(shí)際情況靈活運(yùn)用,切忌不要為了用而用

JDK源碼解析

Calendar 日歷類中,就使用到了簡單工廠模式,下面我們寫一段建議代碼,進(jìn)入 getInstance() 進(jìn)行查看

//JDK源碼解析 - 工廠模式
public class Test {

    public static void main(String[] args) {

        Calendar calendar = Calendar.getInstance();

        System.out.println((calendar.get(Calendar.MONTH) + 1) + "月");//月份下標(biāo)從0開始

        System.out.println(calendar.get(Calendar.DAY_OF_MONTH) + "日");

    }
}

可以看到,源碼中首先定義 Calendar cal = null; 然后通過switch case語句,根據(jù)不同的種類,來創(chuàng)建不同的實(shí)例賦給 cal ,然后返回,和我們簡單工廠模式中,根據(jù)客戶輸入類型的不同,訂購不同的披薩實(shí)例是非常類似的,如圖所示,所以JDK中 Calendar 類給我們返回對象實(shí)例就是經(jīng)典的簡單工廠模式

private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale)
    {
        CalendarProvider provider =
            LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                 .getCalendarProvider();
        if (provider != null) {
            try {
                return provider.getInstance(zone, aLocale);
            } catch (IllegalArgumentException iae) {
                // fall back to the default instantiation
            }
        }

        Calendar cal = null;

        if (aLocale.hasExtensions()) {
            String caltype = aLocale.getUnicodeLocaleType("ca");
            if (caltype != null) {
                switch (caltype) {
                case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                    break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                    break;
                case "gregory":
                    cal = new GregorianCalendar(zone, aLocale);
                    break;
                }
            }
        }

三種工廠模式對比

那到這里,三種工廠模式已經(jīng)全部結(jié)束了,那下面,我把三種模式對比著進(jìn)行總結(jié)與回顧,方便大家融會貫通

簡單工廠模式

定義了一個創(chuàng)建對象的類,由這個類來封裝實(shí)例化對象的行為,可以根據(jù)參數(shù)的不同返回不同類的實(shí)例

簡單工廠模式最大的優(yōu)點(diǎn)在于工廠類中包含了必要的判斷邏輯,根據(jù)客戶端的條件動態(tài)的實(shí)例化相關(guān)的類,對于客戶端來說去除了對具體產(chǎn)品的依賴

工廠方法模式

定義一個創(chuàng)建對象的抽象方法,由子類決定要實(shí)例化的類,將對象的實(shí)例化推遲到子類

工廠方法使一個類的實(shí)例化,延遲到子類中去實(shí)現(xiàn),可以在具體工廠產(chǎn)生實(shí)例的時候,做各種校驗(yàn),和產(chǎn)生規(guī)則,避免了直接用new帶來的不方便控制流程

抽象工廠模式

定義了一個 interface 用于創(chuàng)建相關(guān)或有依賴關(guān)系的一系列對象,而無需指明具體的類

進(jìn)一步的抽象,將工廠抽象成兩層,一個AbsFactory(抽象工廠)和 具體實(shí)現(xiàn)的工廠子類,可以根據(jù)創(chuàng)建對象類型使用對應(yīng)的工廠子類,這樣將單個的簡單工廠類變成了工廠簇,更利于代碼的維護(hù)和擴(kuò)展

工廠模式意義

將實(shí)例化對象的代碼提取出來,放在一個類中統(tǒng)一維護(hù)和管理,達(dá)到和主項(xiàng)目的依賴關(guān)系的解耦,從而提高項(xiàng)目的擴(kuò)展和可維護(hù)性,還是那句話,雖然工廠模式有三種,但是大家要根據(jù)實(shí)際情況靈活運(yùn)用,切忌不要為了用而用

下節(jié)預(yù)告

OK,工廠模式的內(nèi)容到了這里就告一段落了,下一節(jié),我們從大家最感興趣的簡歷入手,開啟原型模式的學(xué)習(xí),最后,希望大家在學(xué)習(xí)的過程中,能夠感覺到設(shè)計(jì)模式的有趣之處,高效而愉快的學(xué)習(xí),那我們下期見~

定義了一個 interface 用于創(chuàng)建相關(guān)或有依賴關(guān)系的一系列對象,而無需指明具體的類

進(jìn)一步的抽象,將工廠抽象成兩層,一個AbsFactory(抽象工廠)和 具體實(shí)現(xiàn)的工廠子類,可以根據(jù)創(chuàng)建對象類型使用對應(yīng)的工廠子類,這樣將單個的簡單工廠類變成了工廠簇,更利于代碼的維護(hù)和擴(kuò)展

工廠模式意義

將實(shí)例化對象的代碼提取出來,放在一個類中統(tǒng)一維護(hù)和管理,達(dá)到和主項(xiàng)目的依賴關(guān)系的解耦,從而提高項(xiàng)目的擴(kuò)展和可維護(hù)性,還是那句話,雖然工廠模式有三種,但是大家要根據(jù)實(shí)際情況靈活運(yùn)用,切忌不要為了用而用

下節(jié)預(yù)告

OK,工廠模式的內(nèi)容到了這里就告一段落了,下一節(jié),我們從大家最感興趣的簡歷入手,開啟原型模式的學(xué)習(xí),最后,希望大家在學(xué)習(xí)的過程中,能夠感覺到設(shè)計(jì)模式的有趣之處,高效而愉快的學(xué)習(xí),那我們下期見~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容