HeadFirst設計模式讀書筆記之工廠模式

来源:https://www.cnblogs.com/Lothlorien/archive/2018/11/28/10029930.html
-Advertisement-
Play Games

1. 簡單工廠 1. 你開了一家披薩店,點披薩的方法可能是這樣: 可以看到,每當你想增加一種披薩類型,就要修改代碼,添加一種if else條件.當有多個系統存在orderPizza的需求時,每個系統都要同時修改他們的代碼.因此,需要將這種實例化具體對象的代碼封裝起來. 這就是簡單工廠方法,他不算一種 ...


1. 簡單工廠

1. 你開了一家披薩店,點披薩的方法可能是這樣:

    public Pizza orderPizza(String type) {
        Pizza pizza;
        if (type.equals("芒果披薩")){
            pizza = new MangoPizza();
        }else if (type.equals("核桃披薩")){
            pizza = new WalnutPizza();
        }else if (type.equals("橙皮披薩")){
            pizza = new FlavedoPizza();
        }else {
            pizza = new StarPizza();
        }

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

可以看到,每當你想增加一種披薩類型,就要修改代碼,添加一種if else條件.當有多個系統存在orderPizza的需求時,每個系統都要同時修改他們的代碼.因此,需要將這種實例化具體對象的代碼封裝起來.

public class PizzaStore {

    SimplePizzaFactory factory;

    public PizzaStore(SimplePizzaFactory factory){
        this.factory = factory;
    }

    public Pizza orderPizza(String type) {
        Pizza pizza;
        pizza = factory.createPizza(type);

        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}
public class SimplePizzaFactory {

    public Pizza createPizza(String type){
        Pizza pizza = null;
        
        if (type.equals("芒果披薩")){
            pizza = new MangoPizza();
        }else if (type.equals("核桃披薩")){
            pizza = new WalnutPizza();
        }else if (type.equals("橙皮披薩")){
            pizza = new FlavedoPizza();
        }else {
            pizza = new StarPizza();
        }
        
        return pizza;
    }
}

這就是簡單工廠方法,他不算一種設計模式,而是一種編程習慣.其要點有二:

1. 將產品定義為抽象類,可以被覆蓋

2. 將具體的產品放到工廠類中來實例化,由工廠創建並返回給客戶,這樣便使得產品的創建邏輯可以解耦合,並增加了復用性

2. 工廠方法

1. 假設你現在有了很多加盟店,每種加盟店根據地區的差異有自己的工廠,但加盟店雖然採用工廠方法創建披薩,但其他部分卻有所不同:烘烤的做法,不切片,使用雜七雜八的盒子...

這時,你想把創建披薩的方法與每家披薩店綁定在一起,讓每家PizzaStore中都使用createPizza()創建pizza,並且這些店還可以擁有一定的製作披薩的自主權.因此,我們需要把PizzaStore和createPizza()定義成抽象的

/**
 * 抽象的PizzaStore
 */
public abstract class PizzaStore {

    public Pizza orderPizza(String type) {
        Pizza pizza;
        // 使用自己的方法做披薩
        pizza = createPizza(type);

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

    // 將做披薩的工廠方法放到PizzaStore中,工廠方法是抽象的,所以依賴子類來處理對象的創建
    public abstract Pizza createPizza(String type);
}

2. 現在由PizzaStore的子類來具體賣披薩

public class BeijingPizzaStore extends PizzaStore {
    @Override
    public Pizza createPizza(String item) {
        if (item.equals("橙皮披薩")){
            return new FlavedoPizza();
        }else {
            return new PekingDuckPizza();
        }
    }
}
public class ChengduPizza extends PizzaStore {
    @Override
    public Pizza createPizza(String type) {
        if (type.equals("芒果披薩")){
            return new MangoPizza();
        }else {
            return new StarPizza();
        }
    }
}

現在,orderPizza()與具體的Pizza實現了一定的解耦合,決定最終Pizza對象的變成了某個具體的pizza店,而pizza店則由顧客來決定

3. 一些對工廠方法的理解:

1. 工廠方法是抽象的,所以依賴子類來創建對象,這樣便將超類的代碼與具體創建產品的代碼分割開了

2. 工廠方法必須返回一個產品,超類通常中會用到這個產品

3. 工廠方法將產品與創建者抽象為介面,使他們成為平級的存在,具體的產品創建則使用子類來交互

4. 工廠方法將產品實例化的操作推遲到子類,子類的選擇則由創建者來決定

5. 簡單工廠方法更趨向組合,工廠方法則利用抽象和繼承

6. 簡單工廠封裝了對象的創建,一個工廠處理所有的產品,但對同一個產品不可變更其做法,工廠方法則更有彈性,子類通過重寫可以更改產品的創建

3. 依賴倒置原則

下麵是一個簡單粗暴的披薩店:

/**
 * 依賴具體對象的PizzaStore
 */
public class PizzaStore {
    
    public Pizza createPizza(String style, String type){
        Pizza pizza = null;
        if (style.equals("一元檔")){
            if (type.equals("芒果披薩")){
                pizza = new MangoPizza();
            }else if (type.equals("核桃披薩")){
                pizza = new WalnutPizza();
            }
        }else if (style.equals("二元檔")){
            if (type.equals("北京烤鴨披薩")){
                pizza = new PekingDuckPizza();
            }else if (type.equals("橙皮披薩")){
                pizza = new FlavedoPizza();
            }
        }else {
            System.out.println("披薩賣完了");
        }
        
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}

可以看到,這個披薩店依賴於每一個具體的披薩對象,每增加一個披薩就要修改一次代碼,增加一個依賴,可以說是非常不好用了

從圖中看到,高層組件直接依賴於低層組件而依賴倒置原則的的內容就是:

要依賴抽象,不要依賴具體類
這個原則說明不管高層還是低層組件都不應該依賴具體類,而應該依賴於抽象.如圖所示:

與之前相比高層組件PizzaStore不在直接依賴於具體的Pizza對象,而是依賴於Pizza介面,而具體的Pizza產品則由被高層組件依賴顛倒成了依賴Pizza介面,這就是一種依賴倒置.總體來看,PizzaStore和Pizza都抽象化了,不用再依賴於具體的類型,符合依賴倒置的設計原則.
有三個指導方針可以讓你更好地遵守依賴倒置原則:

  • 變數不可以持有具體類的引用
  • 不要讓類派生自具體類
  • 不要覆蓋基類中已覆蓋的方法

4. 抽象工廠方法:創建產品家族

與普通的工廠方法比較,抽象工廠方法要顯得重型得多: 工廠方法只創建一種產品,抽象工廠方法則創建一個家族的產品

1. 典型做法是創建一個工廠的介面,這個介面負責創建所有的產品家族成員,如創建一個生產調料家族的工廠:

/**
* 原料工廠的介面,實現這樣的介面,可以隨意組合產出各式各樣的調料
* 每個原料都是一個類
*/
public interface SeasoningFactory {

   Chili createChili();
   
   DouBan createDouBan();
   
   Pepper createPepper();
   
   Salt createSalt();

}
/**
 * 郫縣調料生產廠
 */
public class PiXianSeasoningFactory implements SeasoningFactory {

    /**
     *  虎皮青椒
     * @return
     */
    @Override
    public Chili createChili() {
        return new HuPiQingJiao();
    }

    /**
     * 郫縣豆瓣
     * @return
     */
    @Override
    public DouBan createDouBan() {
        return new PiXianDouBan();
    }

    /**
     * 花椒油
     * @return
     */
    @Override
    public Pepper createPepper() {
        return new HuaJiaoYou();
    }

    /**
     * 食用鹽
     * @return
     */
    @Override
    public Salt createSalt() {
        return new ShiYongYan();
    }
}
/**
 * 灌縣調料生產廠
 */
public class GuanXianSeasoningFactory implements SeasoningFactory {

    /**
     *  虎皮青椒
     * @return
     */
    @Override
    public Chili createChili() {
        return new HuPiQingJiao();
    }

    /**
     * 老乾媽豆瓣醬
     * @return
     */
    @Override
    public DouBan createDouBan() {
        return new LaoGanMaDouBanJiang();
    }

    /**
     * 花椒油
     * @return
     */
    @Override
    public Pepper createPepper() {
        return new HuaJiaoYou();
    }

    /**
     * 食用鹽
     * @return
     */
    @Override
    public Salt createSalt() {
        return new ShiYongYan();
    }
}

下麵是抽象工廠方法的類圖:

從類圖中可以看出來,抽象工廠方法有一個缺點,就是當添加了一個新產品介面時,要去抽象工廠介面中添加一個方法,這會造成一些麻煩

2. 工廠方法與抽象工廠的一些對比

相同點:

  • 都是負責創建對象
  • 都能使程式解耦,將程式從創建大量具體對象的泥潭中拉出來

不同點:

  • 工廠方法用子類來創建具體對象,利用抽象和繼承.抽象工廠包含了一大堆介面,利用組合的思想來創建對象
  • 工廠方法一般用來創建一種產品,抽象工廠則創建一族產品
  • 工廠方法功能簡單,使用輕便.抽象工廠功能強大,使用時會創建大量的類

    3. 下麵使用抽象工廠生產的原料來製作披薩

/**
 * 原料工廠的介面,實現這樣的介面,可以隨意組合產出各式各樣的調料
 * 每個原料都是一個類
 */
public interface SeasoningFactory {

    /**
     * 辣椒
     * @return
     */
    Chili createChili();

    /**
     * 豆瓣
     * @return
     */
    DouBan createDouBan();

    /**
     * 花椒
     * @return
     */
    Pepper createPepper();

    /**
     * 鹽
     * @return
     */
    Salt createSalt();

}
/**
 * 豆瓣
 */
public abstract class DouBan {
    public abstract String sayMyName();
}
public class PiXianDouBan extends DouBan {
    @Override
    public String sayMyName() {
        return "郫縣豆瓣";
    }
}
public class LaoGanMaDouBanJiang extends DouBan {
    @Override
    public String sayMyName() {
        return "老乾媽豆瓣醬";
    }
}
/**
 * 郫縣調料生產廠
 */
public class PiXianSeasoningFactory implements SeasoningFactory {

    /**
     *  虎皮青椒
     * @return
     */
    @Override
    public Chili createChili() {
        return new HuPiQingJiao();
    }

    /**
     * 郫縣豆瓣
     * @return
     */
    @Override
    public DouBan createDouBan() {
        return new PiXianDouBan();
    }

    /**
     * 花椒油
     * @return
     */
    @Override
    public Pepper createPepper() {
        return new HuaJiaoYou();
    }

    /**
     * 食用鹽
     * @return
     */
    @Override
    public Salt createSalt() {
        return new ShiYongYan();
    }
}
/**
 * 灌縣調料生產廠
 */
public class GuanXianSeasoningFactory implements SeasoningFactory {

    /**
     *  虎皮青椒
     * @return
     */
    @Override
    public Chili createChili() {
        return new HuPiQingJiao();
    }

    /**
     * 老乾媽豆瓣醬
     * @return
     */
    @Override
    public DouBan createDouBan() {
        return new LaoGanMaDouBanJiang();
    }

    /**
     * 花椒油
     * @return
     */
    @Override
    public Pepper createPepper() {
        return new HuaJiaoYou();
    }

    /**
     * 食用鹽
     * @return
     */
    @Override
    public Salt createSalt() {
        return new ShiYongYan();
    }
}
/**
 * 每一個披薩都有一組抽象工廠生產的原料
 *
 */
public abstract class Pizza {

    protected String name;

    /**
     * 辣椒
     */
    protected Chili chili;

    /**
     * 豆瓣
     */
    protected DouBan douBan;

    /**
     * 花椒
     */
    protected Pepper pepper;

    /**
     * 鹽
     */
    protected Salt salt;


    /**
     * 在這個方法中,收集抽象工廠生產的原料
     */
    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;
    }

    @Override
    public String toString(){
         return "披薩製作完畢:"+name
        +"\n生產原料:"+chili.sayMyName()
                +"\t"+douBan.sayMyName()
                +"\t"+pepper.sayMyName()
                +"\t"+salt.sayMyName();

    }
}
public class ChengduPizza extends Pizza{

    // 調料工廠
    SeasoningFactory seasoningFactory;

    public ChengduPizza(SeasoningFactory seasoningFactory) {
        this.name = "舊成都披薩";
        this.seasoningFactory = seasoningFactory;
    }

    @Override
    public void prepare() {
        System.out.println("開始準備 "+getName()+":");
        chili = seasoningFactory.createChili();
        douBan = seasoningFactory.createDouBan();
        pepper = seasoningFactory.createPepper();
        salt = seasoningFactory.createSalt();
        System.out.println("以下原料準備完畢:"+chili.sayMyName()
        +","+douBan.sayMyName()+","+pepper.sayMyName()+","+salt.sayMyName());
    }
}
public abstract class PizzaStore {

    public abstract Pizza createPizza(String type);
}
public class StarBoundPizzaStore extends PizzaStore {
    @Override
    public Pizza createPizza(String type) {
        Pizza pizza;
        SeasoningFactory factoryA = new PiXianSeasoningFactory();
        SeasoningFactory factoryB = new GuanXianSeasoningFactory();

        if (type.equals("舊成都披薩")){
            pizza = new ChengduPizza(factoryA);;
        }else if (type.equals("新北京披薩")){
            pizza = new PekingPizza(factoryB);
        }else {
            pizza = new ChengduPizza(factoryB);
        }
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }

    public static void main(String[] args) {
        PizzaStore pizzaStore = new StarBoundPizzaStore();
        Pizza pizza = pizzaStore.createPizza("舊成都披薩");
        System.out.println(pizza.toString());
        Pizza pizza1 = pizzaStore.createPizza("新北京披薩");
        System.out.println(pizza1.toString());
    }
}
結果:
開始準備 舊成都披薩:
以下原料準備完畢:虎皮青椒,郫縣豆瓣,花椒油,食用鹽
烘烤...
切片...
包裝...
披薩製作完畢:舊成都披薩
生產原料:虎皮青椒   郫縣豆瓣    花椒油 食用鹽
開始準備 新北京披薩:
以下原料準備完畢:虎皮青椒,老乾媽豆瓣醬,花椒油,食用鹽
烘烤...
切片...
包裝...
披薩製作完畢:新北京披薩
生產原料:虎皮青椒   老乾媽豆瓣醬  花椒油 食用鹽

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、簡介: GridView是一個以表格形式顯示多張圖片等組件。它是按照行列的方式來顯示內容的,比如實現九宮格圖,用GridView是首選。 <! more 二、代碼塊: 看過我上一篇博客的同學應該知道,一步一步全部步驟寫出來是很費時間的,大概流程就那樣,所以這次網格視圖就直接上代碼塊了,步驟差不多 ...
  • 緣起 前端開發離不開Chrome的開發者工具,尤其是調試Android WebView時。然而,如果使用chrome://Inspect的方法,國內的開發者會驚奇地發現“空白啊”!為此,我發佈過這個離線包的解決方案!已經可以無需Fan牆就能調試了。但是,在使用過程中發現了以下問題: 解決 基於以上問 ...
  • nonatomic : 非原子屬性 atomic : 原子屬性 如果不寫關鍵字 那麼預設就是 原子屬性 - 多線程寫入屬性時,保證同一時間只有一個線程能夠執行寫入操作 - 單(線程)寫多(線程)讀線程技術,同樣有可能出現"臟數據",重新讀一下 - 性能較慢 線程安全 在多個線程進行讀寫操作的時候,仍 ...
  • 單例模式是設計模式中比較常見簡單的一種,典型雙重檢測寫法如下: 接下來對該寫法進行分析,為何這樣寫? 一、為何要同步: 多線程情況下,若是A線程調用getInstance,發現instance為null,那麼它會開始創建實例,如果此時CPU發生時間片切換,線程B開始執行,調用getInstance, ...
  • 數據持久化,也就是把數據保存到磁碟,以後可以再讀取出來使用(也可以再次更改或刪除)。很多場景需要數據持久化,比如為了減輕伺服器的訪問與存儲壓力,客戶端需要在本地做一些數據持久化的工作。iOS的數據持久化,有幾種方式,包括:自定義格式的文件、plistCoreData、FMDB等等。這裡記錄基於Cor... ...
  • 前言 在 "上一篇" 中我們學習了行為型模式的備忘錄模式(Memento Pattern)和狀態模式(Memento Pattern)。本篇則來學習下行為型模式的最後兩個模式,觀察者模式(Observer Pattern)和空對象模式模式(NullObject Pattern)。 觀察者模式 簡介 ...
  • "迭代器模式·原文地址" "更多《設計模式系列教程》" "更多免費教程" 博主按:《每天一個設計模式》旨在初步領會設計模式的精髓,目前採用 ( 靠這吃飯 )和 ( 純粹喜歡 )兩種語言實現。誠然,每種設計模式都有多種實現方式,但此小冊只記錄最直截了當的實現方式 :) 0. 項目地址 迭代器模式源碼: ...
  • 單例模式 介紹 模式:創建型 意圖:保證一個類只有一個實例,並提供一個訪問它的全局訪問點 解決:一個全局使用的類頻繁地創建與銷毀 場景: 唯一序列號 web中的計數器 I/O與資料庫的連接 …… 唯一序列號 web中的計數器 I/O與資料庫的連接 …… 實現方式 餓漢式 :靜態載入,線程安全 餓漢式 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...