設計模式-工廠模式

来源:https://www.cnblogs.com/uniqueDong/archive/2019/07/05/11139365.html
-Advertisement-
Play Games

工廠模式的使用場景、讓自己的代碼解耦更優雅。包含簡單工廠、工廠方法、抽象工廠。一文就夠了 ...


關註公眾號 JavaStorm 獲取更多精彩

工廠模式定義

工廠方法(Factory Method)模式的意義是定義一個創建產品對象的工廠介面,將實際創建工作推遲到子類當中。核心工廠類不再負責產品的創建,這樣核心類成為一個抽象工廠角色,僅負責具體工廠子類必須實現的介面,這樣進一步抽象化的好處是使得工廠方法模式可以使系統在不修改具體工廠角色的情況下引進新的產品。

看下 GOF為工廠模式的定義:

“Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.”(在基類中定義創建對象的一個介面,讓子類決定實例化哪個類。工廠方法讓一個類的實例化延遲到子類中進行。)

工廠模式分類

  • 簡單工廠模式 (Simple Factory),又稱靜態工廠方法模式 (Static Factory Method Pattern)。
  • 工廠方法模式(Factory Method)。
  • 抽象工廠模式(Abstract Factory)。

使用場景

  1. 生產一個發送消息的 產品對象,比如通過郵件、簡訊、微信公眾號等產品發送消息給用戶。
  2. Spring 中 FactoryBean 的 getObject();spring 中 各種各樣的 bean。就可以通過工廠模式創建並且實現了依賴解耦。

工廠模式的優點

  • 解耦:把對象的創建和使用分開。
  • 降低代碼複雜度:如果某個對象的創建比較複雜,或者其過程比較多的步驟。多個地方都會使用就會產生很多重覆代碼
  • 降低維護成本:創建過程有工廠統一管理,當業務發生變化,不需要去找代碼中創建對象 A 的地方組個修改,只要在工廠裡面修改即可。開閉原則。

簡單工廠模式

其實這個並不算設計模式,適合創建簡單對象,創建的對象較少。客戶端不關心對象的創建過程。

簡單工廠模式角色

  • 工廠角色(Factory):簡單工廠模式的核心,負責創建所有實例的內部邏輯,提供外部使用創建所需要的產品。
  • 抽象產品角色(Product):簡單工廠所創建的類型。
  • 具體產品(Concrete Product)角色:簡單工廠模式的創建目標,所有創建的對象都是充當這個角色的某個具體類的實例。

簡單工廠代碼實現

創建發送器介面,也就是產品角色

public interface Sender {
    
    /**
     * 發送信息
     * @param to 收件人
     * @param msg 消息
     * @return
     */
    boolean send(String to, String msg);
    
}

創建具體產品,分別是郵件發送器、簡訊發送器。

public class MailSender implements Sender {

    @Override
    public boolean send(String to, String msg) {
        System.out.println("MailSender:收件人:" + to + ",消息為:" + msg);
        return true;
    }

}


public class SmsSender implements SenderService {

    @Override
    public boolean send(String to, String msg) {
        System.out.println("SmsSender:收件人:" + to + ",消息為:" + msg);
        return true;
    }

}

創建簡單工廠

/**
 * 工廠類,創建實例對象。缺點當字元串輸錯則得不到對象
 * @author unique
 *
 */
public class SendFactory {
    
    public Sender getObject(String type) {
        Sender sender = null;
        switch (type) {
        case "mail":
            senderService = new MailSender();
            break;
        case "sms":
            senderService = new SmsSender();
            break;
        default:
            System.out.println("請輸入正確類型");
            break;
        }
        return sender;
    }

}

單元測試

public class SimpleFactoryTest {
    
    public static void main(String[] args) {
        SendFactory factory = new SendFactory();
        Sender sender = factory.getObject("mail");
        sender.send("大兄弟", "你是最棒的!");
    }

}

缺點

當我們新增產品類的時候,就需要修改工廠類中的 getObject() 方法,不符合 開放-封閉原則。

工廠方法模式

工廠模式中使用最多的一種。

與簡單公差個模式最大的區別就是我們不再提供一個統一的工廠來創建所有的產品,二十針對不同的產品提供不同的工廠。也就是每個產品都有一個與之對應的工廠。

適用場景

  • 一個類不知道它所需要的對象的類:在工廠方法模式中,客戶端不需要知道具體產品類的類名,只需要知道所對應的工廠即可,具體的產品對象由具體工廠類創建;客戶端需要知道創建具體產品的工廠類。
  • 一個類通過其子類來指定創建哪個對象:在工廠方法模式中,對於抽象工廠類只需要提供一個創建產品的介面,而由其子類來確定具體要創建的對象,利用面向對象的多態性和里氏原則。
  • 創建對象的任務委托給多個工廠子類中的某一個,客戶端在使用時可以無需關心是哪一個工廠子類創建產品子類,需要時再動態指定,可將具體工廠類的類名存儲在配置文件或資料庫中。

工廠方法模式角色

  • 抽象工廠(Abstract Factory)角色:是工廠方法模式的核心,與應用程式無關。任何在模式中創建的對象的工廠類必須實現這個介面。
  • 具體工廠(Concrete Factory)角色 :這是實現抽象工廠介面的具體工廠類,包含與應用程式密切相關的邏輯,並且受到應用程式調用以創建某一種產品對象。
  • 抽象產品(AbstractProduct)角色 :工廠方法模式所創建的對象的超類型,也就是產品對象的共同父類或共同擁有的介面。
  • 具體產品(Concrete Product)角色 :這個角色實現了抽象產品角色所定義的介面。某具體產品有專門的具體工廠創建,它們之間往往一一對應

代碼示例

基於上面的簡單工廠,我們改造下。新建一個抽象工廠角色

public interface SenderFactory {
    
    /**
     * 生成對象
     * @return
     */
    public Sender getObject();

}

增加簡訊、郵件工廠類,實現抽象工廠介面。

public class SendMailFactory implements SenderFactory {

    @Override
    public Sender getObject() {
        return new MailSender();
    }

}


public class SendSmsFactory implements SenderFactory {

    @Override
    public Sender getObject() {
        return new SmsSenderServiceImpl();
    }

}

測試代碼

public class Test {

    public static void main(String[] args) {
        SenderFactory senderFactory = new SendMailFactory();
        Sender sender = senderFactory.getObject();
        sender.send("大兄弟", "你是最棒的!");
    }

}

抽象工廠模式

在工廠方法模式中,其實我們有一個潛在意識的意識。那就是我們生產的都是同一類產品。抽象工廠模式是工廠方法的僅一步深化,在這個模式中的工廠類不單單可以創建一種產品,而是可以創建一組產品。這個產品會依賴多個合成一個。比如我們的電腦廠商 有因特爾和AMD 生產的主板與CPU。這個產品族有CPU跟主板。

抽象工廠模式和工廠方法模式一樣,都符合開放-封閉原則。但是不同的是,工廠方法模式在增加一個具體產品的時候,都要增加對應的工廠。但是抽象工廠模式只有在新增一個類型的具體產品時才需要新增工廠。也就是說,工廠方法模式的一個工廠只能創建一個具體產品。而抽象工廠模式的一個工廠可以創建屬於一類類型的多種具體產品。工廠創建產品的個數介於簡單工廠模式和工廠方法模式之間。

適用場景

  • 和工廠方法一樣客戶端不需要知道它所創建的對象的類。
  • 需要一組對象共同完成某種功能時,並且可能存在多組對象完成不同功能的情況。(同屬於同一個產品族的產品)
  • 系統結構穩定,不會頻繁的增加對象。(因為一旦增加就需要修改原有代碼,不符合開閉原則)

抽象工廠方法模式的角色與工廠方法模式一致

我們的電腦有主板、CPU、記憶體…組成。這些都是產品,現在提供一個工廠,直接同時生產出 CPU與對應的主板,避免單獨去創建。也就是所謂的一套,一個系列。

比如 有因特爾工廠出品的 CPU 與 主板,AMD 工廠出品的 CPU 與主板。

代碼示例

定義CPU 與主板兩個產品

public interface CPU {
    void calculate();
}

public interface Mainboard {
    void installCPU();
}

以及產品的具體實現,比如 AMD 公司出品的、因特爾公司出品的 CPU 與主板

public class AmdCpu implements CPU {
    /**
     * CPU的針腳數
     */
    private int pins = 0;

    public AmdCpu(int pins) {
        this.pins = pins;
    }

    @Override
    public void calculate() {
        System.out.println("AMD CPU的針腳數:" + pins);
    }
}


public class IntelCPU implements CPU {
    /**
     * CPU的針腳數
     */
    private int pins = 0;

    public IntelCPU(int pins) {
        this.pins = pins;
    }

    @Override
    public void calculate() {
        System.out.println("Intel CPU的針腳數:" + pins);
    }

}

public class AmdMainboard implements Mainboard {
    /**
     * CPU插槽的孔數
     */
    private int cpuHoles = 0;

    /**
     * 構造方法,傳入CPU插槽的孔數
     *
     * @param cpuHoles
     */
    public AmdMainboard(int cpuHoles) {
        this.cpuHoles = cpuHoles;
    }

    @Override
    public void installCPU() {
        // TODO Auto-generated method stub
        System.out.println("AMD主板的CPU插槽孔數是:" + cpuHoles);
    }
}

public class IntelMainboard implements Mainboard {
    /**
     * CPU插槽的孔數
     */
    private int cpuHoles = 0;

    /**
     * 構造方法,傳入CPU插槽的孔數
     *
     * @param cpuHoles
     */
    public IntelMainboard(int cpuHoles) {
        this.cpuHoles = cpuHoles;
    }

    @Override
    public void installCPU() {
        System.out.println("Intel主板的CPU插槽孔數是:" + cpuHoles);
    }

}

接著我們先創建一個抽象工廠角色,能生產 CPU 與主板系列產品的工廠方法定義。

public interface MainboardCPUFactory {
    /**
     * 創建CPU對象
     *
     * @return CPU對象
     */
    public CPU createCpu();

    /**
     * 創建主板對象
     *
     * @return 主板對象
     */
    public Mainboard createMainboard();
}

因特爾工廠定義

public class IntelFactory implements MainboardCPUFactory {

    @Override
    public CPU createCpu() {
        return new IntelCPU(755);
    }

    @Override
    public Mainboard createMainboard() {
        return new IntelMainboard(755);
    }

}

AMD 工廠

public class AmdFactory implements MainboardCPUFactory {

    @Override
    public CPU createCpu() {
        return new AmdCpu(938);
    }

    @Override
    public Mainboard createMainboard() {
        return new AmdMainboard(938);
    }

}

最後我們來測試

public class AbstactFactoryTest {
    public static void main(String[] args) {
        //使用因特爾工廠生產
        MainboardCPUFactory intelFactory = new IntelFactory();
        CPU cpu = intelFactory.createCpu();
        Mainboard mainboard = intelFactory.createMainboard();

        mainboard.installCPU();
        cpu.calculate();
    }
}

列印

Intel主板的CPU插槽孔數是:755
Intel CPU的針腳數:755

關註公眾號 JavaStorm 給你更多精彩。


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

-Advertisement-
Play Games
更多相關文章
  • display對象可以獲取所有顯示屏此處演示程式啟動是投放新視窗至另一屏幕 import { app, BrowserWindow } from 'electron' const electron = require('electron') //獲取electron對象 let newWindow ...
  • 寫了個評分組件,效果如下 組件Rate.js 組件樣式 Rate.less 背景圖 調用 <Rate number={10} def={5} /> number:為評分總數,預設為5 def:為評分數,預設為0 ...
  • 1. vue項目打包採坑 1.1. vue運行報錯error:Cannot assign to read only property 'exports' of object ' ' 這個錯誤我是在打包完部署到nginx上才會報的,在本地環境可以正常運行,真坑; 網上的資料說的報錯原因是export和 ...
  • 官網地址:http://www.bacubacu.com/colresizable/ 這裡值得註意的是,如果是動態加入的列,則需要先清理調用插件生成的class,id和div之後再重新調用才會有作用。 至於為何動態載入的列沒有效果呢。首先,我想到了可能是方法載入在了動態生成列之前,所以我便手動在生成 ...
  • 雙大括弧會將數據解釋為普通文本,而非 HTML 代碼。為了輸出真正的 HTML,你需要使用 v-html 指令,例如: 渲染結果為: <p>{{message}}</p>里的message被解釋為了普通文本,而不是輸出真正的 HTML,而<p v-html="message"></p>輸出了真正的h ...
  • 這12個問題,基本上就是HTML和CSS基礎中的重點難點了,也是必須要弄清楚的基本問題,其中定位的絕對定位和相對定位到底相對什麼定位?這個還是容易被忽視的,浮動也是一個大坑,有很多細節。 這12個知識點是我個人認為的,下麵我們就來看看這12個知識點。 1.怎麼讓一個不定寬高的 DIV,垂直水平居中? ...
  • SpringCloud系列教程 | 第六篇:Spring Cloud Config Github配置中心 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR1 如無特殊說明,本系列教程全採用以上版本 隨著分散式項目越來越大,勤勞的程式猿們會開始面臨 ...
  • extern可置於變數或者函數前,以表示變數或者函數的定義在別的文件中,提示編譯器遇到此變數和函數時在其他模塊中尋找其定義。 另外,extern也可用來進行鏈接指定。用法分析: 在一個變數前加extern 比如:extern unsigned int Test;編譯器編譯的時候,會把Test當成是外 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...