我的設計模式之旅、09 工廠方法、簡單工廠

来源:https://www.cnblogs.com/linxiaoxu/archive/2022/09/15/16698268.html
-Advertisement-
Play Games

我的設計模式之旅,本節實現武器生產工廠,用Golang實現簡單工廠模式,用C#實現工廠方法模式。對工廠方法模式進行了細緻的介紹。 ...


一個菜鳥的設計模式之旅,文章可能會有不對的地方,懇請大佬指出錯誤。

編程旅途是漫長遙遠的,在不同時刻有不同的感悟,本文會一直更新下去。

程式介紹

由於 Go 中缺少類和繼承等 OOP 特性, 所以無法使用 Go 來實現經典的工廠方法模式。 不過, 我們仍然能實現模式的基礎版本, 即簡單工廠。案例中使用工廠結構體來構建多種類型的武器。因此工廠方法模式代碼使用 C# 表示。

Gun: AK47 gun
Power: 4
Gun: Musket gun
Power: 1

程式代碼、簡單工廠模式 Golang

首先, 創建一個名為 i­Gun的介面, 其中將定義一支槍所需具備的所有方法。 然後是實現了 iGun 介面的 gun槍支結構體類型。 兩種具體的槍支——ak47mus­ket火槍 ——兩者都嵌入了槍支結構體, 且間接實現了所有的 i­Gun方法。

gun­Fac­to­ry槍支工廠結構體將發揮工廠的作用, 即通過傳入參數構建所需類型的槍支。 main.go 則扮演著客戶端的角色。 其不會直接與 ak47mus­ket進行互動, 而是依靠 gun­Fac­to­ry來創建多種槍支的實例, 僅使用字元參數來控制生產。

iGun.go: 產品介面

package main

type IGun interface {
    setName(name string)
    setPower(power int)
    getName() string
    getPower() int
}

gun.go: 具體產品

package main

type Gun struct {
    name  string
    power int
}

func (g *Gun) setName(name string) {
    g.name = name
}

func (g *Gun) getName() string {
    return g.name
}

func (g *Gun) setPower(power int) {
    g.power = power
}

func (g *Gun) getPower() int {
    return g.power
}

ak47.go: 具體產品

package main

type Ak47 struct {
    Gun
}

func newAk47() IGun {
    return &Ak47{
        Gun: Gun{
            name:  "AK47 gun",
            power: 4,
        },
    }
}

musket.go: 具體產品

package main

type musket struct {
    Gun
}

func newMusket() IGun {
    return &musket{
        Gun: Gun{
            name:  "Musket gun",
            power: 1,
        },
    }
}

gunFactory.go: 工廠

package main

import "fmt"

func getGun(gunType string) (IGun, error) {
    if gunType == "ak47" {
        return newAk47(), nil
    }
    if gunType == "musket" {
        return newMusket(), nil
    }
    return nil, fmt.Errorf("Wrong gun type passed")
}

main.go: 客戶端代碼

package main

import "fmt"

func main() {
    ak47, _ := getGun("ak47")
    musket, _ := getGun("musket")

    printDetails(ak47)
    printDetails(musket)
}

func printDetails(g IGun) {
    fmt.Printf("Gun: %s", g.getName())
    fmt.Println()
    fmt.Printf("Power: %d", g.getPower())
    fmt.Println()
}

Console: 輸出

Gun: AK47 gun
Power: 4
Gun: Musket gun
Power: 1

程式代碼、工廠方法模式 C#

image-20220915230253913

程式功能和簡單工廠相同,用工廠方法模式實現。

Gun.cs: 武器類

namespace 工廠方法;

public abstract class Gun
{
    protected Gun(string name, int power)
    {
        Name = name;
        Power = power;
    }

    public string Name { get; private set; }

    public int Power { get; private set; }

    public void setName(string name)
    {
        Name = name;
    }

    public void setPower(int power)
    {
        Power = power;
    }
}

public class AK47 : Gun
{
    public AK47() : base("ak47", 10)
    {
    }
}

public class Musket : Gun
{
    public Musket() : base("musket", 5)
    {
    }
}

Factory.cs: 工廠類

namespace 工廠方法;

public interface GunFactory
{
    Gun createGun();
}

public class AK47Factory : GunFactory
{
    public Gun createGun()
    {
        Console.WriteLine("正在生產AK47");
        return new AK47();
    }
}

public class MusketFactory : GunFactory
{
    public Gun createGun()
    {
        Console.WriteLine("正在生產Musket");
        return new Musket();
    }
}

Program.cs: 客戶端代碼

using 工廠方法;

GunFactory gunFactory = new AK47Factory();

Gun ak47_1 = gunFactory.createGun();
Gun ak47_2 = gunFactory.createGun();

gunFactory = new MusketFactory();

Gun Musket_1 = gunFactory.createGun();

List<Gun> guns = new List<Gun>() { ak47_1, ak47_2, Musket_1 };

foreach (Gun gun in guns)
{
    Console.WriteLine($"武器名字:{gun.Name}");
    Console.WriteLine($"武器傷害:{gun.Power}");
}

Console: 輸出

正在生產AK47
正在生產AK47
正在生產Musket
武器名字:ak47
武器傷害:10
武器名字:ak47
武器傷害:10
武器名字:musket
武器傷害:5

思考總結

什麼是簡單工廠模式

簡單工廠模式也是工廠模式的一種,但不屬於23種設計模式。

目的使客戶端與產品解耦。將產品創建實例過程從客戶端代碼中獨立出去。生成具體對象的邏輯判斷也從客戶端分離至簡單工廠類中。

簡單工廠模式的最大優點在於工廠類中包含了必要的邏輯判斷,根據客戶端的選擇條件動態實例化相關的類,對於客戶端來說,去除了與具體產品的依賴。

但簡單工廠違背了開放-封閉原則,導致每次添加新類別的時候,都需要去修改工廠類的分支case判斷,可以用反射動態生成實例解決。

簡單工廠反射案例:我的設計模式之旅、01 策略模式、簡單工廠、反射 - 小能日記

什麼是工廠方法模式

工廠方法是一種創建型設計模式, 其在父類中提供一個創建對象的方法,允許子類決定實例化對象的類型。

工廠方法模式:定義一個用於創建對象的介面,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到子類。

image-20220915214645465

工廠方法類 (創建者類):主要職責並非是創建產品。其中通常會包含一些核心業務邏輯,這些邏輯依賴於由工廠方法返回的產品對象。子類可通過重寫工廠方法並使其返回不同類型的產品來間接修改業務邏輯。

主要解決:主要解決介面選擇的問題。客戶端將所有產品視為抽象的介面,客戶端知道所有產品都提供該介面要求的方法,並不關心其具體實現方式。

何時使用:

  • 我們明確地計劃不同條件創建不同實例時。
  • 當你在編寫代碼的過程中,如果無法預知對象確切類別及其依賴關係時,可使用工廠方法。
  • 希望用戶能擴展你軟體庫或框架的內部組件,可使用工廠方法。
  • 希望復用現有對象來節省系統資源,而不是每次都重新創建對象,可使用工廠方法。在處理大型資源密集型對象(資料庫連接、文件系統和網路資源)時,你會經常碰到這種資源需求。
  • 在許多設計工作的初期都會使用工廠方法(較為簡單,而且可以更方便地通過子類進行定製),隨後演化為使用抽象工 廠、原型或生成器(更靈活但更加複雜)。

如何解決:讓其子類實現工廠介面,返回的也是一個抽象的產品。使用特殊的工廠方法代替對於對象構造函數new的直接調用。

實現步驟:

  1. 讓所有產品遵循同一介面。改介面必須聲明對所有產品都有意義的方法。
  2. 在工廠方法類(創建類中)添加一個空的工廠方法。該方法的返回類型必須遵循通用的產品介面。
  3. 找到對於產品構造函數的所有引用。依次替換為對工廠方法的調用,並將創建產品的代碼移入工廠方法。(類似簡單工廠,此時switch分支是龐大的)
  4. 為每種產品編寫創建者子類,在子類中重寫工廠方法,將基本方法的相關創建代碼移動到工廠方法中。子類過多時,可以給子類創建者傳入參數,判斷生成。
  5. 如果代碼經過上述移動後,基礎工廠方法中已經沒有任何代碼,你可以將其轉變為抽象類。如果基礎工廠方法中還有其他語句,你可以將其設置為該方法的預設行為。一般總是把創建者基類作為抽象類。

關鍵代碼:創建過程在其子類執行。

應用實例:

  • 您需要一輛汽車,可以直接從工廠裡面提貨,而不用去管這輛汽車是怎麼做出來的,以及這個汽車裡面的具體實現。
  • Hibernate 換資料庫只需換方言和驅動就可以。

優點:

  • 一個調用者想創建一個對象,只要知道其名稱就可以了。
  • 開閉原則。擴展性高,如果想增加一個產品,只要擴展一個工廠類就可以。
  • 屏蔽產品的具體實現,調用者只關心產品的介面。避免創建者和具體產品之間的緊密耦合。
  • 單一職責原則。你可以將產品創建代碼放在程式的單一位置, 從而使得代碼更容易維護。

缺點:每次增加一個產品時,都需要增加一個具體類和對象實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的複雜度,同時也增加了系統具體類的依賴。這並不是什麼好事。

使用場景:

  • 日誌記錄器:記錄可能記錄到本地硬碟、系統事件、遠程伺服器等,用戶可以選擇記錄日誌到什麼地方。
  • 資料庫訪問,當用戶不知道最後系統採用哪一類資料庫,以及資料庫可能有變化時。
  • 設計一個連接伺服器的框架,需要三個協議,"POP3"、"IMAP"、"HTTP",可以把這三個作為產品類,共同實現一個介面。

註意事項:

  • 作為一種創建類模式,在任何需要生成複雜對象的地方,都可以使用工廠方法模式。有一點需要註意的地方就是複雜對象適合使用工廠模式,而簡單對象,特別是只需要通過 new 就可以完成創建的對象,無需使用工廠模式。如果使用工廠模式,就需要引入一個工廠類,會增加系統的複雜度。

  • 創建對象的邏輯判斷依舊在客戶端中實現。

與其他模式的關係:

  • 抽象工廠模式通常基於一組工廠方法,但你也可以使用原型模式來生成這些類的方法。
  • 你可以同時使用工廠方法和迭代器來讓子類集合返回不同類型的迭代器,並使得迭代器與集合相匹配。
  • 工廠方法是模板方法的一種特殊形式。同時,工廠方法可以作為一個大型模板方法中的一個步驟。

思考對象復用的方法

  1. 首先,你需要創建存儲空間來存放所有已經創建的對象。

  2. 當他人請求一個對象時,程式將在對象池中搜索可用對象。

  3. 然後將其返回給客戶端代碼。

  4. 如果沒有可用對象,程式則創建一個新對象(並將其添加到對象池中)。

四個步驟的代碼必須位於同一處,確保重覆代碼不會污染程式!

參考資料


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

-Advertisement-
Play Games
更多相關文章
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 最近每天學習的時候,發現了一道很有趣的面試題 1.const [a, b] = { a: 100, b: 200 } 2.console.log(a) 3.console.log(b) 如何在不改變1的情況下,讓代碼不報錯 這個時候,我突 ...
  • 前端像素鳥小游戲 點擊打開視頻講解更加詳細 一、案例效果 二、實現思路 創建游戲背景板和小鳥,並分別設置相對定位與絕對定位; 初始化背景圖的位置; 初始化小鳥的位置; 設置游戲狀態,游戲開始時背景和管道全部向左運動,游戲結束全部停止運動; 使小鳥飛行,其實就是背景圖在 X 軸方向的位置不斷減小,實現 ...
  • 一、案例效果 點擊打開視頻講解更加詳細 二、實現思路 創建游戲背景板; 創建我方戰機,滑鼠進入游戲面板後其隨滑鼠軌跡運動; onmousemove 創建子彈,讓子彈周期性的在戰機處發出並讓其向 top 值減小的方向(向上)移動,top 小於 0 也就是子彈走出游戲面板時刪除自身; 創建敵機,讓敵機周 ...
  • 話說新冠3年,“狀態管理框架”豪傑並起、群雄逐鹿,ReduxToolkit、Mobx、Vuex、Pinia、Dva、Rematch、Recoil、Zustand、Mirror...敢問英雄獨鐘哪廂? ...
  • 每日3題 1 以下代碼執行後,控制臺中的輸出內容為? // 以下代碼執行後,控制臺中輸出的內容是什麼 var fullname = "a"; var obj = { fullname: "b", prop: { fullname: "c", getFullname: function () { re ...
  • 樣式獲取 style屬性 只能獲取標簽內容style屬性裡面存在的一些樣式 如果你需要獲取對應的全局所有地方設置樣式 我們就需要採用一些方法 getComputedStyle 方法屬於window的方法 Window.getComputedStyle()方法返回一個對象,該對象在應用活動樣式表並解析 ...
  • 前言 使用 Vite 已經有兩年了,期間使用它開發過單頁面應用,也開發過瀏覽器擴展插件,對比日常工作中用到的 webpack 構建速度大幅提升,開發體驗也好很多。 雖然相比於 webpack 來說簡單了很多,但是仍然有一些配置需要記錄一下,以便之後可以快速搭建一個本地開發構建的環境。 使用 crea ...
  • 1、spring Cloud概述 Spring Cloud是一系列框架的有序集合。它利用Spring Boot的開發便利性巧妙地簡化了分散式系統基礎設施的開發,如服務發現註冊、配置中心、消息匯流排、負載均衡、斷路器、數據監控等,都可以用Spring Boot的開發風格做到一鍵啟動和部署。Spring ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...