初識設計模式 - 工廠模式

来源:https://www.cnblogs.com/fatedeity/archive/2022/08/30/16638128.html
-Advertisement-
Play Games

創建對象可能會導致大量的重覆代碼,可能會需要複合對象訪問不到的信息,也可能提供不了足夠級別的抽象,還可能並不是複合對象概念的一部分。工廠設計模式通過定義一個單獨的創建對象的方法來解決這些問題。 ...


簡介

工廠設計模式(Factory Design Pattern)是一種創建型的設計模式,它提供了一種創建對象的最佳方式,是一種代替 new 操作符的一種模式。

在工廠模式中,創建對象不會對客戶端暴露創建邏輯,而是通過使用一個共同的介面來指向新創建的對象。

工廠模式還可以細分為三種的類型:簡單工廠模式、工廠方法模式和抽象工廠模式。

簡單工廠模式

概念

簡單工廠模式(Simple Factory Pattern)的定義是,由一個工廠對象決定創建出哪一種產品類的實例,被創建的產品類實例具有共同的父類或實現同樣的介面。

因為在簡單工廠模式中用於創建實例的方法通常是靜態方法,因此簡單工廠模式又被稱為靜態工廠模式(Static Factory Pattern)。

實現方式

下麵是使用果汁生產來展示簡單工廠模式:

果汁 FruitJuice 介面:描述生產果汁的必要方法

public interface FruitJuice {
    void make();
}

蘋果汁 AppleJuice 類:生產蘋果汁的類

public class AppleJuice implements FruitJuice {
    public AppleJuice() {
        this.make();
    }

    @Override
    public void make() {
        System.out.println("This is AppleJuice make!");
    }
}

橙汁 OrangeJuice 類:生產橙汁的類

public class OrangeJuice implements FruitJuice {
    public OrangeJuice() {
        this.make();
    }

    @Override
    public void make() {
        System.out.println("This is OrangeJuice make!");
    }
}

果汁工廠 FruitJuiceFactory 類:負責生產各種果汁的類

public class FruitJuiceFactory {
    public FruitJuiceFactory() {}

    public static FruitJuice createFruitJuice(String juiceType) {
        FruitJuice fruitJuice = null;
        if (juiceType.equals("AppleJuice")) {
            fruitJuice = new AppleJuice();
        } else if (juiceType.equals("OrangeJuice")) {
            fruitJuice = new OrangeJuice();
        }
        return fruitJuice;
    }
}

通過 if-else 邏輯是簡單工廠的第一種實現方法,還可以通過靜態代碼塊和 Map 結構實現簡單工廠模式,具體的代碼示例如下:

import java.util.Map;
import java.util.HashMap;

public class FruitJuiceFactory {
    // 存儲產品類的映射關係
    private static final Map<String, FruitJuice> cachedFruitJuice = new HashMap<>();

    static {
        cachedFruitJuice.put("AppleJuice", new AppleJuice());
        cachedFruitJuice.put("OrangeJuice", new OrangeJuice());
    }

    public FruitJuiceFactory() {}

    public static FruitJuice createFruitJuice(String juiceType) {
        if (juiceType == null || juiceType.isEmpty()) {
            return null;
        }
        // 直接從 Map 結構中取到對應的產品類實例
        return cachedFruitJuice.get(juiceType.toLowerCase());
    }
}

優點

簡單工廠模式的主要優點如下:

  • 簡單工廠模式實現了對象創建和使用的分離
  • 客戶端無需知道所創建的具體產品類的類名,只需要知道具體產品類所對應的參數即可
  • 可以在不改變客戶端代碼的情況下更換或增加新的具體產品類

缺點

簡單工廠模式的主要缺點如下:

  • 工廠類集中了相似產品類的創建邏輯,職責過重
  • 簡單工廠模式違反開閉原則,新增產品類時需要改動到工廠類
  • 通常使用靜態方法作為創建實例的方法,無法實現繼承關係

適用場景

簡單工廠模式的適用場景如下:

  • 對於一批產品類,並且不會新增產品類,可以選擇簡單工廠模式

源碼

在 JDK 中,java.util.Calendar 使用了簡單工廠模式。如下是其的一些實現邏輯:

public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {
    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) {
                cal = switch (caltype) {
                    case "buddhist" -> new BuddhistCalendar(zone, aLocale);
                    case "japanese" -> new JapaneseImperialCalendar(zone, aLocale);
                    case "gregory"  -> new GregorianCalendar(zone, aLocale);
                    default         -> null;
                };
            }
        }
        if (cal == null) {
            if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                cal = new BuddhistCalendar(zone, aLocale);
            } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                       && aLocale.getCountry() == "JP") {
                cal = new JapaneseImperialCalendar(zone, aLocale);
            } else {
                cal = new GregorianCalendar(zone, aLocale);
            }
        }
        return cal;
    }
}

工廠方法模式

概念

簡單工廠模式違背了開閉原則,沒有能夠做到“對擴展開放、對修改關閉”,新增產品類時需要改動到工廠類。

而工廠方法模式(Factory Method Pattern)是對簡單工廠模式的進一步抽象,其好處是可以使系統在不修改原來代碼的情況下引入新的產品類,即滿足開閉原則。

和簡單工廠模式中工廠負責生產所有的產品相比,工廠方法模式抽象出簡單工廠的介面,然後將生產具體產品的任務分發給具體的產品工廠,即簡單工廠的工廠。

由於工廠方法模式利用了面向對象的多態特性,因此又被稱為多態工廠模式(Polymorphic Factory Pattern)。

實現方式

在上述簡單工廠模式的基礎上,將果汁生產改造成工廠方法模式:

抽象工廠 AbstractFactory 介面:描述生產果汁工廠的必要方法

public interface FruitJuiceFactory {
    FruitJuice createFruitJuice();
}

蘋果汁工廠 AppleJuiceFactory 類:詳細的生產蘋果汁的類

public class AppleJuiceFactory implements FruitJuiceFactory {
    public AppleJuiceFactory() {}

    @Override
    public FruitJuice createFruitJuice() {
        return new AppleJuice();
    }
}

橙汁工廠 OrangeJuiceFactory 類:詳細的生產橙汁的類

public class OrangeJuiceFactory implements FruitJuiceFactory {
    public OrangeJuiceFactory() {}

    @Override
    public FruitJuice createFruitJuice() {
        return new OrangeJuice();
    }
}

優點

工廠方法模式除了具有簡單工廠模式的優點之外,還有以下優點:

  • 在系統中增加新的產品類時,無需修改原有的工廠代碼,只需添加一個具體產品類和具體工廠類

缺點

工廠方法模式的主要缺點如下:

  • 添加新的產品類時,需要同時提供具體產品類和對應的工廠類,系統中類的個數將成對增加
  • 工廠方法模式引入了抽象層,增加了系統的抽象性和理解難度

適用場景

工廠方法模式的適用場景如下:

  • 客戶端知道其需要的介面實現類,不知道所需要的具體對象類,可以通過對應的工廠創建實例

源碼

在 JDK 中,java.util.Collection 介面中定義的 iterator() 方法就是一個工廠方法。

所有實現了 Collection 介面的類,都需要顯式地實現此方法並返回一個 Iterator 實例,不同的實現類可以擁有自己的實現邏輯。

抽象工廠模式

概念

在工廠方法模式中,具體工廠負責生產具體的產品,每個具體工廠對應一種具體產品,工廠方法具有唯一性。

抽象工廠模式(Abstract Factory Pattern)定義了一個介面用於創建相關或有依賴關係的對象族,而無需指明具體的類,其可以整合簡單工廠模式和抽象工廠模式。

學習抽象工廠模式需要理解兩個概念:產品等級結構指的是產品的繼承結構,如抽象果汁和具體果汁之間構成一個產品等級結構;產品族指的是由同一個工廠生產的,位於不同等級結構中的一組產品,如同一個飲料工廠生產的酒和果汁構成一個產品族。

從上述概念上理解,工廠方法模式針對的是一個產品等級結構,而抽象工廠模式在工廠方法模式的基礎上,覆蓋了多個工廠的產品族。

實現方式

在上述簡單工廠模式的基礎上,下麵使用果汁生產和酒生產來展示抽象工廠模式:

酒 Alcohol 介面:描述生產酒的必要方法

public interface Alcohol {
    void make();
}

葡萄酒 Wine 類:詳細的生產葡萄酒的類

public class Wine implements Alcohol {
    public Wine() {
        this.make();
    }

    @Override
    public void make() {
        System.out.println("This is Wine make!");
    }
}

啤酒 Beer 類:詳細的生產啤酒的類

public class Beer implements Alcohol {
    public Beer() {
        this.make();
    }

    @Override
    public void make() {
        System.out.println("This is Beer make!");
    }
}

抽象工廠 AbstractFactory 介面:描述生產果汁和酒的必要方法

public interface AbstractFactory {
    FruitJuice creatFruitJuice();
    Alcohol createAlcohol();
}

具體工廠 ConcreteFactory 類:可生產果汁和酒的工廠的類

public class ConcreteFactory implements AbstractFactory {
    public ConcreteFactory() {}

    @Override
    public FruitJuice creatFruitJuice(String juiceType) {
        FruitJuice fruitJuice = null;
        if (juiceType.equals("AppleJuice")) {
            fruitJuice = new AppleJuice();
        } else if (juiceType.equals("OrangeJuice")) {
            fruitJuice = new OrangeJuice();
        }
        return fruitJuice;
    }

    @Override
    public Alcohol createAlcohol(String alcoholType) {
        Alcohol alcohol = null;
        if (alcoholType.equals("Wine")) {
            alcohol = new Wine();
        } else if (alcoholType.equals("Beer")) {
            alcohol = new Beer();
        }
        return alcohol;
    }
}

優點

抽象工廠模式的主要優點如下:

  • 當一個產品族中的多個對象被設計成一起工作時,它能夠保證客戶端始終只使用同一個產品族中的對象
  • 增加新的產品族很方便,無需修改已有代碼,符合開閉原則

缺點

抽象工廠模式的主要缺點如下:

  • 增加新的產品等級結構麻煩,需要對原有系統進行較大的修改,甚至需要修改抽象層代碼

適用場景

抽象工廠模式的適用場景如下:

  • 當需要創建的對象是一系列相互關聯或相互依賴的產品族時,便可以使用抽象工廠模式
  • 產品等級結構穩定,設計完成之後,不會向系統中增加新的產品等級結構或者刪除已有的產品等級結構

源碼

在 JDK 中,java.util.Collection 介面可以看作一個抽象工廠。

Collection 介面定義了轉換成 Iterator 實例的工廠方法,也定義了轉換成 T[] 實例的工廠方法,可以認定其做了兩個產品族的定義。

總結

使用標準

對於要不要使用工廠模式,其最本質的參考標準有以下四個:

  • 封裝變化:創建邏輯有可能變化,封裝成工廠類之後,創建邏輯的變更對調用者透明
  • 代碼復用:創建代碼抽離到獨立的工廠類之後可以復用
  • 隔離複雜性:封裝複雜的創建邏輯,調用者無需瞭解如何創建對象
  • 控制複雜度:將創建代碼抽離出來,讓原本的函數或類職責更單一,代碼更簡潔

應用場景

對於產品種類相對較少、且可預見性地不會修改的情況,可以使用簡單工廠模式。使用簡單工廠模式的客戶端只需傳入工廠類的參數,不需要關心如何創建對象的邏輯,可以很方便地創建所需產品。

由於簡單工廠模式會將所有創建邏輯都放在一個工廠類中,會導致這個工廠類會變得很複雜,當產品的種類是可預見地會增加時,還需要對工廠類做更改,這種時候可以採用工廠方法模式以達到不需要修改原來代碼的情況下引進新的產品。

抽象工廠模式最早的應用是用於創建屬於不同操作系統的視窗構件。當需要創建的對象是一系列相互關聯或相互依賴的產品族時,或者是只會新增新的產品族而不是新種類的產品時,可以使用抽象工廠模式。

首發於翔仔的個人博客,點擊查看更多。


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

-Advertisement-
Play Games
更多相關文章
  • 進入/離開 & 列表過渡 點擊打開視頻講解更加詳細 概述 Vue 在插入、更新或者移除 DOM 時,提供多種不同方式的應用過渡效果。包括以下工具: 在 CSS 過渡和動畫中自動應用 class 可以配合使用第三方 CSS 動畫庫,如 Animate.css 在過渡鉤子函數中使用 JavaScript ...
  • 本文是深入淺出 ahooks 源碼系列文章的第十五篇,該系列已整理成文檔-地址。覺得還不錯,給個 star 支持一下哈,Thanks。 本篇接著針對關於 DOM 的各個 Hook 封裝進行解讀。 useFullscreen 管理 DOM 全屏的 Hook。 該 hook 主要是依賴 screenfu ...
  • Windows的靈魂是什麼?當然是Window,當方便快捷的多視窗進入人們視野的時候,大家無不為之驚呼太好用了!! ...
  • 類型 |類型 | 例子| 描述 | | | | | | number | 1,2,-2 | 任意數字 | | string | 'hi',"hi" | 任意字元串 | | boolean | true,false | 布爾值或者true false | | 字面量 | 其本身 | 限制變數的值就是該 ...
  • 在各種短視頻界面上,我們經常會看到類似這樣的點贊動畫: 非常的有意思,有意思的交互會讓用戶更願意進行互動。 那麼,這麼有趣的點贊動畫,有沒有可能使用純 CSS 實現呢?那當然是必須的,本文,就將巧妙的藉助 transition,僅僅使用 CSS 完成這麼一個點贊動畫。 實現不同表情的不斷上升 如果使 ...
  • 在設計稿轉網頁中運用基於self-attention機制設計的機器學習模型進行設計稿的佈局,能夠結合dom節點的上下文得出合理的方案. ...
  • 代碼的執行流程分為順序,分支和迴圈三種結構,順序結構是預設的,判斷結構主要有if-else和switch-case兩種,迴圈結構有while,do-while,for三種,其中continue和break是跳出迴圈. ...
  • 蒼穹之邊,浩瀚之摯,眰恦之美; 悟心悟性,善始善終,惟善惟道! —— 朝槿《朝槿兮年說》 寫在開頭 從接觸 Java 開發到現在,大家對 Java 最直觀的印象是什麼呢?是它宣傳的 “Write once, run anywhere”,還是目前看已經有些過於形式主義的語法呢?有沒有靜下心來仔細想過, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...