用C# (.NET Core) 實現抽象工廠設計模式

来源:https://www.cnblogs.com/cgzl/archive/2018/04/10/8776868.html
-Advertisement-
Play Games

本文的概念性內容來自深入淺出設計模式一書. 上一篇文章講了簡單工廠和工廠方法設計模式 http://www.cnblogs.com/cgzl/p/8760250.html, 使用的是披薩店的例子. 文將繼續使用這個例子, 這裡要用到抽象工廠. 披薩店的需求變更 現在披薩店在各地授權了很多連鎖分店, ...


本文的概念性內容來自深入淺出設計模式一書.

上一篇文章講了簡單工廠和工廠方法設計模式 http://www.cnblogs.com/cgzl/p/8760250.html, 使用的是披薩店的例子.

文將繼續使用這個例子, 這裡要用到抽象工廠.

披薩店的需求變更

現在披薩店在各地授權了很多連鎖分店, 但是有的分店偷工減料, 使用劣質原料代替標準原料.

披薩店老闆現在就是想解決這個問題.

原料的一致性問題

首先如何保證原料的質量問題? 可以建立一個工廠生產原料, 然後把原料分發到各地的授權店.

然後還有一個原料的一致性問題, 例如紐約的番茄醬和芝加哥的番茄醬可能有點不同, 所以它們各自需要一套原料.

也就是說各地的披薩是使用相同的原料, 但是每種原料在各地可能會存在差異(不同的實現).

這就是紐約, 芝加哥和加州各自的原料家族.

建立原料工廠

接下來就是建立原料工廠, 這些工廠將負責為各自的家族(地點)創建原料.

首先是工廠的介面:

然後我們要做下麵這些內容:

  • 為每個地區創建一個工廠 (實現PizzaIngredientFactory介面及其方法)
  • 實現一些原料的類, 它們可以唄工廠使用, 其中某些原料可以跨地區共用
  • 最後我們把上面這些整合到PizzaStore裡面.

紐約的原料工廠:

就是實現介面, 返回本地需要的原料而已.

修改Pizza抽象類:

這裡我們把Prepare()方法(準備原料)改成抽象的了, 其它的保持不變.

接下來需要為各地創建不同風格的披薩了. 現在各地披薩店的原料都是從工廠送來的, 就不能使用劣質原料代替了.

之前使用工廠方法模式時, 我們為每個地點創建了不同風格的披薩, 例如 NYCheesePizza, ChicagoCheesePizza. 你可以看一下這兩個類, 它們裡面只有原料部分(都是同樣的原料, 但是各地風格不同)是不同的.

所以實際上, 我們不需要為每個地點創建不同風格的披薩, 原料工廠將會替我們處理各地風格披薩原料不同這種情況.

例如乳酪披薩只需要一個類就可以:

為了創建乳酪披薩, 在其構造函數裡面傳入原料工廠為它提供原料即可.

在prepare()方法裡面準備的原料都是工廠來提供的.

使用哪些地區/風格的原料由工廠決定, 披薩類本身並不關心, 它只需知道怎麼製作披薩就行.

這樣披薩類和各地區的原材料就解耦了.

綜上, 就是一句話:

原料由工廠提供.

可以再看看另外一個披薩的例子:

修改各地的披薩店

紐約的披薩店現在和紐約的原料工廠組合在一起, 這樣它就可以產出紐約風格的披薩了.

在創建披薩的時候把原料工廠傳進去為披薩提供原料.

到目前位置, 我們做了什麼?

我們提供了一種可以為披薩提供一族原料的工廠, 這個工廠就叫做抽象工廠.

抽象工廠為創建某一家族的產品提供介面(interface), 針對這個介面編程, 就可以實現從具體生產產品的工廠解耦.

這樣做就允許我們為不同的上下文使用不同實現的工廠了.

因為我們的代碼是從實際產品解耦的, 我們就可以通過替換工廠來取得不同風格的產品了.

梳理一下整個流程

1. 創建紐約的披薩店:

2. 下訂單買披薩

3. orderPizza方法調用創建披薩的方法:

到這, 代碼都沒有變化.

4.創建披薩的時候, 使用原料工廠:

5. 披薩的準備工序里是由工廠來提供原料:

6. 按照其他工序加工並返回披薩.

抽象工廠定義

抽象工廠設計模式提供了一個介面, 這個介面可以創建一族相關或依賴的對象而無需指明它們具體的類.

下麵是類圖:

對應披薩店的圖:

工廠方法和抽象工廠的比較

工廠方法是通過繼承來實現創建對象工作的. 而抽象工廠則是通過組合的方法.

工廠方法是讓子類來創建對象, 客戶只需要知道抽象類, 子類做具體的實現, 解耦.

抽象工廠提供了一個可以創建一族產品的抽象類, 這個類的實現類/子類決定產品是如何產出的, 也是解耦.

抽象工廠的優點是: 可以創建一族相關的產品. 缺點是它的介面比較大, 如果添加產品了需要改介面.

而工廠方法只負責生產一個產品.

抽象工廠也經常使用工廠方法來實現具體的工廠.

而工廠方法也經常使用抽象的創造者, 它來使用子類創造出的具體產品.

工廠方法:

抽象工廠:

總結

C#/.NET Core代碼實現

 原料介面:

namespace AbstractFactoryPattern.Abstractions
{
    public interface IGredient
    {
        string Name { get; }
    }
}

namespace AbstractFactoryPattern.Abstractions
{
    public interface ICheese: IGredient
    {
    }
}

namespace AbstractFactoryPattern.Abstractions
{
    public interface IClams: IGredient
    {
    }
}

namespace AbstractFactoryPattern.Abstractions
{
    public interface IDough: IGredient
    {
    }
}

namespace AbstractFactoryPattern.Abstractions
{
    public interface ISauce: IGredient
    {
    }
}
View Code

 

原料工廠介面:

namespace AbstractFactoryPattern.Abstractions
{
    public interface IPizzaIngredientFactory
    {
        IDough CreateDough();
        ICheese CreateCheese();
        IClams CreateClams();
        ISauce CreateSauce();
    }
}
View Code

 

披薩店抽象類:

namespace AbstractFactoryPattern.Abstractions
{
    public abstract class PizzaStore
    {
        public Pizza OrderPizza(string type)
        {
            var pizza = CreatePizza(type);
            pizza.Prepare();
            pizza.Bake();
            pizza.Cut();
            pizza.Box();
            return pizza;
        }

        protected abstract Pizza CreatePizza(string type);
    }
}
View Code

 

披薩抽象類:

using System;

namespace AbstractFactoryPattern.Abstractions
{
    public abstract class Pizza
    {
        public string Name { get; set; }
        public IDough Dough { get; protected set; }
        public ISauce Sauce { get; protected set; }
        public ICheese Cheese { get; protected set; }
        public IClams Clams { get; protected set; }

        public abstract void Prepare();

        public void Bake()
        {
            Console.WriteLine("Bake for 25 minutes");
        }

        public void Cut()
        {
            Console.WriteLine("Cutting the pizza into diagnol slices");
        }

        public void Box()
        {
            Console.WriteLine("Placing pizza in official PizzaStore box......");
        }
    }
}
View Code

 

具體原料:

using AbstractFactoryPattern.Abstractions;

namespace AbstractFactoryPattern.Ingredients
{
    public class FreshClams : IClams
    {
        public string Name { get; } = "Fresh Clams";
    }
}

using AbstractFactoryPattern.Abstractions;

namespace AbstractFactoryPattern.Ingredients
{
    public class FrozenClams: IClams
    {
        public string Name { get; } = "Frozen Clams";
    }
}

using AbstractFactoryPattern.Abstractions;

namespace AbstractFactoryPattern.Ingredients
{
    public class MarinaraSauce: ISauce
    {
        public string Name { get; } = "Marinara Sauce";
    }
}

using AbstractFactoryPattern.Abstractions;

namespace AbstractFactoryPattern.Ingredients
{
    public class MozzarellaCheese: ICheese
    {
        public string Name { get; } = "Mozzarella Cheese";
    }
}

using AbstractFactoryPattern.Abstractions;

namespace AbstractFactoryPattern.Ingredients
{
    public class PlumTomatoSauce : ISauce
    {
        public string Name { get; } = "Plum Tomato Sauce";
    }
}

using AbstractFactoryPattern.Abstractions;

namespace AbstractFactoryPattern.Ingredients
{
    public class ReggianoCheese : ICheese
    {
        public string Name { get; } = "Reggiano Cheese";
    }
}

using AbstractFactoryPattern.Abstractions;

namespace AbstractFactoryPattern.Ingredients
{
    public class ThickCrustDough: IDough
    {
        public string Name { get; } = "Thick Crust Dough";
    }
}

using AbstractFactoryPattern.Abstractions;

namespace AbstractFactoryPattern.Ingredients
{
    public class ThinCrustDough: IDough
    {
        public string Name { get; } = "Thin Crust Dough";
    }
}
View Code

 

具體披薩:

using System;
using AbstractFactoryPattern.Abstractions;

namespace AbstractFactoryPattern.ConcreteProducts
{
    public class CheesePizza: Pizza
    {
        private readonly IPizzaIngredientFactory _pizzaIngredientFactory;

        public CheesePizza(IPizzaIngredientFactory pizzaIngredientFactory)
        {
            _pizzaIngredientFactory = pizzaIngredientFactory;
        }

        public override void Prepare()
        {
            Console.WriteLine($"Preparing: {Name}");
            Dough = _pizzaIngredientFactory.CreateDough();
            Sauce = _pizzaIngredientFactory.CreateSauce();
            Clams = _pizzaIngredientFactory.CreateClams();
            Cheese = _pizzaIngredientFactory.CreateCheese();
            Console.WriteLine($"    {Dough.Name}");
            Console.WriteLine($"    {Sauce.Name}");
            Console.WriteLine($"    {Clams.Name}");
            Console.WriteLine($"    {Cheese.Name}");
        }
    }
}

using System;
using AbstractFactoryPattern.Abstractions;

namespace AbstractFactoryPattern.ConcreteProducts
{
    public class ClamsPizza : Pizza
    {
        private readonly IPizzaIngredientFactory _pizzaIngredientFactory;

        public ClamsPizza(IPizzaIngredientFactory pizzaIngredientFactory)
        {
            _pizzaIngredientFactory = pizzaIngredientFactory;
        }

        public override void Prepare()
        {
            Console.WriteLine($"Preparing: {Name}");
            Dough = _pizzaIngredientFactory.CreateDough();
            Sauce = _pizzaIngredientFactory.CreateSauce();
            Clams = _pizzaIngredientFactory.CreateClams();
            Console.WriteLine($"    {Dough.Name}");
            Console.WriteLine($"    {Sauce.Name}");
            Console.WriteLine($"    {Clams.Name}");
        }
    }
}
View Code

 

各地原料工廠:

using AbstractFactoryPattern.Abstractions;
using AbstractFactoryPattern.Ingredients;

namespace AbstractFactoryPattern.ConcreteFactories
{
    public class ChicagoPizzaIngredientFactory: IPizzaIngredientFactory
    {
        public IDough CreateDough()
        {
            return new ThinCrustDough();
        }

        public ICheese CreateCheese()
        {
            return new ReggianoCheese();
        }

        public IClams CreateClams()
        {
            return new FrozenClams();
        }

        public ISauce CreateSauce()
        {
            return new PlumTomatoSauce();
        }
    }
}

using AbstractFactoryPattern.Abstractions;
using AbstractFactoryPattern.Ingredients;

namespace AbstractFactoryPattern.ConcreteFactories
{
    public class NewYorkPizzaIngredientFactory: IPizzaIngredientFactory
    {
        public IDough CreateDough()
        {
            return new ThickCrustDough();
        }

        public ICheese CreateCheese()
        {
            return new MozzarellaCheese();
        }

        public IClams CreateClams()
        {
            return new FreshClams();
        }

        public ISauce CreateSauce()
        {
            return new MarinaraSauce();
        }
    }
}
View Code

 

各地披薩店:

using AbstractFactoryPattern.Abstractions;
using AbstractFactoryPattern.ConcreteFactories;
using AbstractFactoryPattern.ConcreteProducts;

namespace AbstractFactoryPattern.Clients
{
    public class ChicagoPizzaStore : PizzaStore
    {
        protected override Pizza CreatePizza(string type)
        {
            var factory = new ChicagoPizzaIngredientFactory();
            Pizza pizza = null;
            switch (type)
            {
                case "cheese":
                    pizza = new CheesePizza(factory);
                    pizza.Name = "Chicago Cheese Pizza";
                    break;
                case "clams":
                    pizza = new ClamsPizza(factory);
                    pizza.Name = "Chicago Clams Pizza";
                    break;
            }
            return pizza;
        }
    }
}

using AbstractFactoryPattern.Abstractions;
using AbstractFactoryPattern.ConcreteFactories;
using AbstractFactoryPattern.ConcreteProducts;

namespace AbstractFactoryPattern.Clients
{
    public class NewYorkPizzaStore : PizzaStore
    {
        protected override Pizza CreatePizza(string type)
        {
            var factory = new NewYorkPizzaIngredientFactory();
            Pizza pizza = null;
            switch (type)
            {
                case "cheese":
                    pizza = new CheesePizza(factory);
                    pizza.Name = "New York Cheese Pizza";
                    break;
                case "clams":
                    pizza = new ClamsPizza(factory);
                    pizza.Name = "New York Clams Pizza";
                    break;
            }
            return pizza;
        }
    }
}
View Code

 

測試運行:

using System;
using AbstractFactoryPattern.Clients;

namespace AbstractFactoryPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            var newYorkPizzaStore = new NewYorkPizzaStore();
            newYorkPizzaStore.OrderPizza("cheese");

            Console.WriteLine("-----------------------------------------------------------");

            var chicagoYorkPizzaStore = new ChicagoPizzaStore();
            chicagoYorkPizzaStore.OrderPizza("cheese");

            Console.ReadKey();
        }
    }
}
View Code

 

Ok.


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

-Advertisement-
Play Games
更多相關文章
  • 1.連接SQLServer,創建資料庫TestDB; 2.添加EF引用,點擊工具-NuGet包管理器-管理解決方案的NuGet程式包, 搜索EntityFramework包,點擊安裝; 3.在Web.config中添加節點 其中Data Source為伺服器名,Initial Catalog為剛纔在 ...
  • 當年面試一家公司,面試官問我:一個int類型的數組怎麼獲取裡面第二大的數字? 給了我一張紙和一支筆讓我寫一個方法,我想了想便寫了一個方法: 然後問我能不能優化一下你的代碼,我當然回答可以了,尷尬的就是我想了半天也想不出來,當時腦子一片空白........; 最後無奈的說:可以優化,但是我現在想不到! ...
  • 需求: 最近項目里遇到了個問題,對方沒有提供直接獲取數據源的api介面,而是通過郵件發數據的。這就需要接收郵件後解析郵件內容獲得數據。 分析: 想在代碼里實時獲取郵件,可以通過郵件的幾個協議來實現,常用的幾個協議有POP3協議,SMTP協議,IAMP協議。 1. pop3:(Post Office ...
  • Linq 雖然用得多,但是裡面有一些方法比較少用,因此整理一下。Enumerable 類的所有方法可以在 MSDN 上查閱到:https://msdn.microsoft.com/zh-cn/library/system.linq.enumerable.aspx Aggregate 這個方法有三個重 ...
  • 批註內容可以是對某段文字或內容的註釋,也可以是對文段中心思想的概括提要,或者是對文章內容的評判、疑問,以及在閱讀時給自己或他人起到提示作用。本篇文章中將介紹如何在C#中操作Word批註,主要包含以下要點: 插入Word批註 修改Word批註 刪除Word批註 使用工具:Free Spire.Doc ...
  • 新建用戶控制項取名:UserControl1,UserControl1繼承自上面新建的介面IObjectSafety,並實現介面(實現介面的方法請複製下麵的內容),在類UserControl1添加Guid特性值,利用VS的工具生成guid 進行簡單界面佈局: 點擊按鈕效果見上面代碼,還可自己寫一個公共 ...
  • MVC和三層架構 (有錯誤的話歡迎來噴,沒包袱) 一、概念 1、 MVC 最初的概念 輸入(控制器)、輸出(視圖)、邏輯處理(模型) 2、 三層架構 是對原始MVC概念中邏輯處理層(模型層)的進一步劃分,分UI-業務邏輯BLL-數據訪問DAL。三層里的模型是數據模型,與資料庫對應 但如今 MVC與三 ...
  • 上一篇文章中我們學習了列表頁面的結構,@page與@model兩個關鍵Razor指令,以及頁面佈局應該修改哪裡。這一篇文章我們來學習一下新建頁面。 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...