設(shè)計(jì)模式——抽象工廠模式

《Head First 設(shè)計(jì)模式》 學(xué)習(xí)筆記,碼云同步更新中

如有錯(cuò)誤或不足之處,請(qǐng)一定指出,謝謝~

目錄

查看其它設(shè)計(jì)模式筆記,點(diǎn)這里→設(shè)計(jì)模式筆記匯總

抽象工廠模式(Abstract Factory Pattern)

  • 定義:
    • 提供一個(gè)創(chuàng)建一系列相關(guān)或相互依賴(lài)對(duì)象的接口,用于創(chuàng)建相關(guān)或依賴(lài)對(duì)象的家族,而無(wú)須指定它們具體的類(lèi)。
  • 結(jié)構(gòu):
    • AbstractFactory:抽象工廠
    • ConcreteFactory:具體工廠
    • AbstractProduct:抽象產(chǎn)品
    • ConcreteProduct:具體產(chǎn)品
  • 對(duì)比工廠方法和簡(jiǎn)單工廠:
    • 工廠方法模式針對(duì)的是一個(gè)產(chǎn)品等級(jí)結(jié)構(gòu),而抽象工廠模式則需要面對(duì)多個(gè)產(chǎn)品等級(jí)結(jié)構(gòu),一個(gè)工廠等級(jí)結(jié)構(gòu)可以負(fù)責(zé)多個(gè)不同產(chǎn)品等級(jí)結(jié)構(gòu)中的產(chǎn)品對(duì)象的創(chuàng)建 。當(dāng)一個(gè)工廠等級(jí)結(jié)構(gòu)可以創(chuàng)建出分屬于不同產(chǎn)品等級(jí)結(jié)構(gòu)的一個(gè)產(chǎn)品族中的所有對(duì)象時(shí),抽象工廠模式比工廠方法模式更為簡(jiǎn)單、有效率。
    • 當(dāng)抽象工廠模式中每一個(gè)具體工廠只創(chuàng)建一個(gè)產(chǎn)品對(duì)象,那抽象工廠模式就退化為工廠方法模式。
    • 當(dāng)工廠方法模式的抽象工廠與具體工廠合并,提供一個(gè)統(tǒng)一的工廠來(lái)創(chuàng)建產(chǎn)品對(duì)象,那工廠方法模式就退化為簡(jiǎn)單工廠。
  • 優(yōu)點(diǎn):
    • 新增加具體工廠和產(chǎn)品族很方便,符合“開(kāi)閉”原則
    • 符合“依賴(lài)倒置”原則
    • 高層模塊不需要關(guān)心低層模塊的具體實(shí)現(xiàn),只需要關(guān)心抽象,關(guān)心工廠。
    • 抽象工廠可以約束產(chǎn)品族的特性(比如配比,芝士與奶油1:2)
  • 缺點(diǎn):
    • 對(duì)于產(chǎn)品族的擴(kuò)展非常困難。抽象工廠中定義了有可能會(huì)被創(chuàng)建的所有產(chǎn)品的方法。如果新增產(chǎn)品,就必須要修改抽象類(lèi)和所有實(shí)現(xiàn)類(lèi)。(“開(kāi)閉原則”的傾斜性)
  • 使用場(chǎng)景:
    • 系統(tǒng)中有多個(gè)產(chǎn)品族,每次只使用其中某一族
    • 系統(tǒng)需要約束屬于同一產(chǎn)品族的產(chǎn)品必須在一起使用
  • 案例:
    • 簡(jiǎn)單工廠:顧客點(diǎn)單時(shí),需要根據(jù)菜名創(chuàng)建出各種不同種類(lèi)的披薩。這些披薩都源自同一個(gè)披薩基類(lèi),不過(guò)各自有自己的口味實(shí)現(xiàn)。而我們創(chuàng)建具體的披薩對(duì)象時(shí),只需要提供名字,不需要知道它們是如何創(chuàng)建的。這時(shí)就可以用到簡(jiǎn)單工廠,傳遞一個(gè)參數(shù)給工廠類(lèi),返回一個(gè)相應(yīng)的披薩對(duì)象。
    • 工廠方法:這時(shí)我們披薩店要擴(kuò)張了,但在各個(gè)省可能會(huì)有不同的制作方法。我們希望披薩在披薩口味上加盟店有自己的決定權(quán)。但在制作流程上要受到總店的控制,比如必須使用我們品牌的盒子來(lái)裝披薩等等。
      這時(shí)候就需要我們用工廠方法模式來(lái)改造了。
    • ok,又有新需求了。有些加盟店會(huì)使用廉價(jià)的原料來(lái)增加利潤(rùn)。你想要更多的質(zhì)量控制——建造一家原料工廠,將原料運(yùn)送到加盟店。同時(shí)問(wèn)題也出現(xiàn)了,各地加盟店的披薩口味不同,原料也是不一樣的。這時(shí)候抽象工廠模式就有用武之地了。
  • 代碼:
/**
 * 披薩原料抽象工廠
 **/
public interface PizzaIngredientFactory {

    Cheese createCheese();

    Pepperoni createPepperoni();
}

/**
 * 意大利香腸原料
 **/
public interface Pepperoni {
    // 略...
}

/**
 * 芝士原料
 **/
public interface Cheese {
    // 略...
}

/**
 * 上海用的芝士
 **/
public class SHCheese implements Cheese {
    // 略...
}

/**
 * 上海用的香腸
 **/
public class SHPepperoni implements Pepperoni {
    // 略...
}


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

    String name;

    public Cheese cheese;

    public Pepperoni pepperoni;

    /**
     * 準(zhǔn)備
     * 申明為抽象方法,子類(lèi)需自行實(shí)現(xiàn)原料準(zhǔn)備
     */
    public abstract void prepare();

    /**
     * 烘焙
     */
    public void bake() {
        System.out.println("烘焙中...");
    }

    /**
     * 切片
     */
    public void cut() {
        System.out.println("切片中...");
    }

    /**
     * 裝盒
     */
    public void box() {
        System.out.println("裝盒中...");
    }


    public String getName() {
        return name;
    }

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

/**
 * 芝士披薩
 **/
public class CheesePizza extends Pizza {

    PizzaIngredientFactory ingredientFactory;

    /**
     * 構(gòu)造器注入工廠
     *
     * @param ingredientFactory
     */
    public CheesePizza(PizzaIngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }

    /**
     * pizza無(wú)需關(guān)心到底是什么類(lèi)型的原料工廠,只要是原料工廠就行
     */
    @Override
    public void prepare() {
        System.out.println("原料準(zhǔn)備中");
        // 為基類(lèi)屬性賦值
        cheese = ingredientFactory.createCheese();
        pepperoni = ingredientFactory.createPepperoni();
    }
}

/**
 * 披薩店 抽象
 **/
public abstract class PizzaStore {

    public Pizza orderPizza(String type) {

        // 把創(chuàng)建披薩方法從簡(jiǎn)單工廠拿回來(lái)
        Pizza pizza = createPizza(type);

        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        System.out.println(pizza.getName() + "制作完成");

        return pizza;
    }

    /**
     * 將實(shí)例化披薩的責(zé)任移到抽象方法中,這個(gè)方法就等同于一個(gè)工廠
     * 子類(lèi)通過(guò)這個(gè)方法來(lái)執(zhí)行對(duì)象實(shí)例化邏輯,達(dá)到超類(lèi)和子類(lèi)解耦的目的
     */
    abstract Pizza createPizza(String type);
}

/**
 * 上海加盟店
 **/
public class SHPizzaStore extends PizzaStore {

    /**
     * 上海披薩原料工廠
     */
    private PizzaIngredientFactory ingredientFactory = new SHPizzaIngredientFactory();

    private Pizza pizza;

    @Override
    protected Pizza createPizza(String type) {
        if ("cheese".equals(type)) {
            pizza = new CheesePizza(ingredientFactory);
            pizza.setName("上海芝士披薩");
            return pizza;
        } else if ("veggie".equals(type)) {
            // 略...
            return null;
        }
        return null;
    }
}

/**
 * 測(cè)試類(lèi)
 **/
public class Test {
    public static void main(String[] args) {
        SHPizzaStore shPizzaStore = new SHPizzaStore();
        shPizzaStore.orderPizza("cheese");
    }
}

結(jié)果:
    原料準(zhǔn)備中
        原料:上海芝士
        原料:上海臘腸
    烘焙中...
    切片中...
    裝盒中...
    上海芝士披薩制作完成
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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