用C#(.NET Core) 实现简单工厂和工厂方法设计模式
本文源自深入淺出設(shè)計(jì)模式. 只不過我是使用C#/.NET Core實(shí)現(xiàn)的例子.
?
前言
當(dāng)你看見new這個(gè)關(guān)鍵字的時(shí)候, 就應(yīng)該想到它是具體的實(shí)現(xiàn).
這就是一個(gè)具體的類, 為了更靈活, 我們應(yīng)該使用的是接口(interface).
有時(shí)候, 你可能會(huì)寫出這樣的代碼:
這里有多個(gè)具體的類被實(shí)例化了, 是根據(jù)不同情況在運(yùn)行時(shí)被實(shí)例化的.?
當(dāng)你看到這樣的代碼, 你就會(huì)知道當(dāng)有需求需要對其進(jìn)行修改或者擴(kuò)展的時(shí)候, 你就得把這個(gè)文件打開, 然后看看在這里應(yīng)該添加或者刪除點(diǎn)什么. 這類的代碼經(jīng)常會(huì)分散在程序的多個(gè)地方, 這維護(hù)和更新起來就很麻煩而且容易出錯(cuò).
針對interface進(jìn)行編程的時(shí)候, 你知道可以把自己獨(dú)立于系統(tǒng)未來可能要發(fā)生的變化. 為什么呢? 因?yàn)槿绻汜槍nterface編程, 那么對于任何實(shí)現(xiàn)了該接口的具體類對你來說都可以用, 多態(tài)嗎.
項(xiàng)目原始需求
有一個(gè)前沿的披薩店, 做披薩, 下面是訂購披薩的類:
new一個(gè)披薩, 然后按照工序進(jìn)行加工 最后返回披薩.
但是, 一個(gè)披薩店不可能只有一種披薩, 可能會(huì)有很多中披薩, 所以你可能會(huì)這樣修改代碼:
根據(jù)傳入的類型, 創(chuàng)建不同的披薩, 然后加工返回.
然后問題來了, 隨著時(shí)間的推移, 一個(gè)披薩店會(huì)淘汰不暢銷的披薩并添加新品種披薩.
使用上面的代碼就會(huì)出現(xiàn)這個(gè)問題, 針對需求變化, 我不得不把OrderPizza的部分代碼改來改去:
從這里, 我們也可以看到, 上半部分是會(huì)變化的部分, 下半部分是不變的部分, 所以它們應(yīng)該分開(把變化的部分和不變的部分分開, 然后進(jìn)行封裝).
結(jié)構(gòu)應(yīng)該是這樣的:
右上角是變化的部分, 把這部分封裝到一個(gè)對象里, 它就是用來創(chuàng)建披薩的對象, 我們把這個(gè)對象叫做:?工廠.
工廠負(fù)責(zé)創(chuàng)建對象的細(xì)節(jié)工作. 我們創(chuàng)建的這個(gè)工廠叫做SimplePizzaFactory, 而orderPizza()這個(gè)方法就是該工廠的一個(gè)客戶(client).
任何時(shí)候客戶需要披薩的時(shí)候, 披薩工廠就會(huì)給客戶創(chuàng)建一個(gè)披薩.
接下來, 我們就建立這個(gè)簡易的披薩工廠:
就是通過傳入的類型參數(shù), 建立并返回不同類型的披薩.
這樣我們就把披薩創(chuàng)建的工作封裝到了一個(gè)類里面, 發(fā)生變化的時(shí)候, 只需要修改這一個(gè)類即可.
注意: 有時(shí)候上面這種簡單工廠可以使用靜態(tài)方法, 但是這樣也有缺點(diǎn), 就是無法通過繼承來擴(kuò)展這個(gè)工廠了.
回來修改PizzaStore這個(gè)類:
工廠是從構(gòu)造函數(shù)傳入的, 并在PizzaStore里面保留一個(gè)引用.
在OrderPizza()方法里面, 我們使用工廠的創(chuàng)建方法代替了new關(guān)鍵字, 所以在這里沒有具體的實(shí)例化.
簡單工廠的定義
簡單/簡易工廠并不是一個(gè)設(shè)計(jì)模式, 更多是一個(gè)編程習(xí)慣. 但是使用的非常廣泛.
簡單工廠類圖:
這個(gè)很簡單, 就不解釋了.?
簡單工廠就到這, 下面要講兩個(gè)重量級的工廠模式.
用C#/.NET Core實(shí)現(xiàn)簡單工廠
Pizza父類:
using System;
using System.Collections.Generic;
namespace SimpleFactory.Pizzas
{
? ? public abstract class Pizza
? ? {
? ? ? ? public string Name { get; protected set; }
? ? ? ? public string Dough { get; protected set; }
? ? ? ? public string Sauce { get; protected set; }
? ? ? ? protected List<string> Toppings = new List<string>();
? ? ? ? public void Prepare()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine($"Preparing: {Name}");
? ? ? ? ? ? Console.WriteLine($"Tossing: {Dough}");
? ? ? ? ? ? Console.WriteLine($"Adding sauce: {Sauce}");
? ? ? ? ? ? Console.WriteLine("Adding toppings: ");
? ? ? ? ? ? Toppings.ForEach(x => Console.WriteLine($"? {x}"));
? ? ? ? }
? ? ? ? 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......");
? ? ? ? }
? ? }
}
各種Pizza:
namespace SimpleFactory.Pizzas
{
? ? public class CheesePizza: Pizza
? ? {
? ? ? ? public CheesePizza()
? ? ? ? {
? ? ? ? ? ? Name = "Cheese Pizza";
? ? ? ? ? ? Dough = "Think Dough";
? ? ? ? ? ? Sauce = "Salad";
? ? ? ? ? ? Toppings.Add("Grated Reggiano Cheese");
? ? ? ? }
? ? }
}
namespace SimpleFactory.Pizzas
{
? ? public class ClamPizza: Pizza
? ? {
? ? ? ? public ClamPizza()
? ? ? ? {
? ? ? ? ? ? Name = "Clam Pizza";
? ? ? ? ? ? Sauce = "Tomato sauce";
? ? ? ? ? ? Dough = "Soft dough";
? ? ? ? ? ? Toppings.Add("Shrimp meat");
? ? ? ? }
? ? }
}
namespace SimpleFactory.Pizzas
{
? ? public class PepperoniPizza: Pizza
? ? {
? ? ? ? public PepperoniPizza()
? ? ? ? {
? ? ? ? ? ? Name = "Pepperoni Pizza";
? ? ? ? ? ? Dough = "Thin dough";
? ? ? ? ? ? Sauce = "Black pepper";
? ? ? ? ? ? Toppings.Add("Beef Granules");
? ? ? ? ? ? Toppings.Add("Niblet");
? ? ? ? }
? ? }
}
簡單工廠:
using SimpleFactory.Pizzas;
namespace SimpleFactory
{
? ? public class SimplePizzaFactory
? ? {
? ? ? ? public Pizza CreatePizza(string type)
? ? ? ? {
? ? ? ? ? ? Pizza pizza = null;
? ? ? ? ? ? switch (type)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? case "cheese":
? ? ? ? ? ? ? ? ? ? pizza = new CheesePizza();
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? case "pepperoni":
? ? ? ? ? ? ? ? ? ? pizza = new PepperoniPizza();
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? case "clam":
? ? ? ? ? ? ? ? ? ? pizza = new ClamPizza();
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? ? ? return pizza;
? ? ? ? }
? ? }
}
PizzaStore:
using SimpleFactory.Pizzas;
namespace SimpleFactory
{
? ? public class PizzaStore
? ? {
? ? ? ? private readonly SimplePizzaFactory _factory;
? ? ? ? public PizzaStore(SimplePizzaFactory factory)
? ? ? ? {
? ? ? ? ? ? _factory = factory;
? ? ? ? }
? ? ? ? public Pizza OrderPizza(string type)
? ? ? ? {
? ? ? ? ? ? var pizza = _factory.CreatePizza(type);
? ? ? ? ? ? pizza.Prepare();
? ? ? ? ? ? pizza.Bake();
? ? ? ? ? ? pizza.Cut();
? ? ? ? ? ? pizza.Box();
? ? ? ? ? ? return pizza;
? ? ? ? }
? ? }
}
測試運(yùn)行:
using System;
namespace SimpleFactory
{
? ? class Program
? ? {
? ? ? ? static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? var pizzaStore = new PizzaStore(new SimplePizzaFactory());
? ? ? ? ? ? var cheesePizza = pizzaStore.OrderPizza("cheese");
? ? ? ? ? ? Console.WriteLine();
? ? ? ? ? ? var clamPizza = pizzaStore.OrderPizza("pepperoni");
? ? ? ? ? ? Console.ReadKey();
? ? ? ? }
? ? }
}
?
需求變更 - 授權(quán)連鎖披薩店
?披薩店開的很好, 所以老板在全國各地開授權(quán)連鎖分店了, 而每個(gè)地點(diǎn)的分店根據(jù)當(dāng)?shù)鼐用竦目谖? 它們所提供的披薩種類可能會(huì)不同.
例如紐約和芝加哥和加利福尼亞的就有可能不同.
針對這個(gè)需求, 我們可能會(huì)想到的第一種辦法就是: 把SimplePizzaFactory抽取出來, 分別建立三個(gè)地點(diǎn)的工廠, 然后根據(jù)地點(diǎn)把相應(yīng)的工廠組合到PizzaStore
代碼是這樣的:
紐約:
芝加哥:
因?yàn)閭€(gè)連鎖店分布在各地, 老板想做質(zhì)量管控: 做披薩的基本工序應(yīng)該是一樣的, 但是針對某種披薩各地可以有不同的風(fēng)格做法.
所以我們把createPizza()方法放回到PizzaStore, 但這次它是抽象方法, 然后各地都會(huì)創(chuàng)建自己的PIzzaStore:
下面是紐約和芝加哥的披薩店:
針對每種披薩, 紐約和芝加哥可能會(huì)有自己風(fēng)格具體實(shí)現(xiàn)的披薩.
orderPizza()方法是在父類/抽象類里面實(shí)現(xiàn)的, 這里的披薩還是抽象的, 所以它并不知道是PizzaStore的哪個(gè)子類來做的披薩.
代碼運(yùn)行的時(shí)候, orderPizza()會(huì)調(diào)用createPizza()方法, PizzaStore的某個(gè)子類肯定會(huì)對此負(fù)責(zé).
所以你哪個(gè)地方的PizzaStore, 就會(huì)決定產(chǎn)出的是哪個(gè)地方特產(chǎn)的披薩.
?
下面就創(chuàng)建PizzaStore, 例如紐約的:
其他地點(diǎn)的都差不多, 就不貼圖了.
如何聲明一個(gè)工廠方法
還是看這張圖:
抽象的PizzaStore把訂購披薩的固定工序orderPizza()放在了抽象類里面.
創(chuàng)建披薩createPizza()方法是在各地的披薩店里做實(shí)現(xiàn).
用一行代碼來解釋工廠方法就是:
工廠方法是讓其子類具體來實(shí)現(xiàn)對象創(chuàng)建的工作. 這樣就把父類中的客戶代碼和子類的創(chuàng)建對象部分的代碼解耦了.
?
上面工作做的挺好, 但是還差一件事....披薩.
首先抽象父類:
里面定義了調(diào)味料和工序
然后具體的披薩:
紐約的奶酪披薩
芝加哥的奶酪披薩
最后運(yùn)行一下:
?
工廠方法模式
所有的工廠模式都會(huì)封裝對象的創(chuàng)建過程, 而工廠方法模式把對象創(chuàng)建的動(dòng)作交給了子類, 并讓它決定創(chuàng)建哪些對象.
創(chuàng)建者:
?
產(chǎn)品:
看看另外一種結(jié)構(gòu) -- 并行的類結(jié)構(gòu):
?
工廠方法模式的定義:
工廠方法模式定義了一個(gè)創(chuàng)建對象的接口, 但是讓子類來決定具體創(chuàng)建的是哪一個(gè)對象. 工廠方法讓一個(gè)類延遲實(shí)例化, 直到子類的出現(xiàn).
左邊是產(chǎn)品, 所有具體的產(chǎn)品都應(yīng)該繼承于同一個(gè)父類/接口.
右邊的Creator類里面包含所有方法的實(shí)現(xiàn)除了抽象的工廠方法. 這個(gè)抽象的工廠方法在Creator的子類里面必須進(jìn)行實(shí)現(xiàn), 產(chǎn)品就是在子類具體實(shí)現(xiàn)的工廠方法里面創(chuàng)造出來的.
?
設(shè)計(jì)原則 -- 應(yīng)該依賴于抽象, 而不依賴于具體的類
這就是著名的:?DIP (Dependency Inversion Principle) 依賴反轉(zhuǎn)原則.
進(jìn)一步解釋就是:?高級別的組件不應(yīng)該依賴于低級別的組件, 它們都應(yīng)該依賴于抽線.
高級別組件, 就是它有一組行為定義在另外一堆低級別的組件里面了.
例如PizzaStore就是高級別的, 具體的披薩就是低級別的.
應(yīng)該該設(shè)計(jì)原則后:
這時(shí)它們都依賴于抽象的披薩父類了.
實(shí)現(xiàn)該原則的三點(diǎn)指導(dǎo)建議
沒有變量引用具體的類(可已使用工廠代替創(chuàng)建這個(gè)具體的類)
沒有類派生于具體的類(派生于它就依賴于它)
不去重寫(override)其任一父類的已實(shí)現(xiàn)方法(如果重寫了, 那么這個(gè)類并不適合作為起始的抽象類, 因?yàn)榛惱锩娴姆椒ū緫?yīng)該是共享與所有子類的)
和其它原則一樣, 只是盡力去按照這三點(diǎn)建議去執(zhí)行, 并不是必須一直要這么做.
C#/.NET Core的代碼實(shí)現(xiàn)
各種pizza:
namespace FactoryMethodPattern.Pizzas
{
? ? public class ChicagoCheesePizza : Pizza
? ? {
? ? ? ? public ChicagoCheesePizza()
? ? ? ? {
? ? ? ? ? ? Name = "Chicago Cheese Pizza";
? ? ? ? ? ? Dough = "Think Dough 1";
? ? ? ? ? ? Sauce = "Salad 1";
? ? ? ? ? ? Toppings.Add("Grated Reggiano Cheese 1");
? ? ? ? }
? ? }
}
namespace FactoryMethodPattern.Pizzas
{
? ? public class ChicagoClamPizza : Pizza
? ? {
? ? ? ? public ChicagoClamPizza()
? ? ? ? {
? ? ? ? ? ? Name = "Chicago Clam Pizza";
? ? ? ? ? ? Sauce = "Tomato sauce 1";
? ? ? ? ? ? Dough = "Soft dough 1";
? ? ? ? ? ? Toppings.Add("Shrimp meat 1");
? ? ? ? }
? ? }
}
namespace FactoryMethodPattern.Pizzas
{
? ? public class ChicagoPepperoniPizza : Pizza
? ? {
? ? ? ? public ChicagoPepperoniPizza()
? ? ? ? {
? ? ? ? ? ? Name = "Chicago Pepperoni Pizza";
? ? ? ? ? ? Dough = "Thin dough 1";
? ? ? ? ? ? Sauce = "Black pepper 1";
? ? ? ? ? ? Toppings.Add("Beef Granules 1");
? ? ? ? ? ? Toppings.Add("Niblet 1");
? ? ? ? }
? ? }
}
namespace FactoryMethodPattern.Pizzas
{
? ? public class NYCheesePizza: Pizza
? ? {
? ? ? ? public NYCheesePizza()
? ? ? ? {
? ? ? ? ? ? Name = "NY Cheese Pizza";
? ? ? ? ? ? Dough = "Think Dough 2";
? ? ? ? ? ? Sauce = "Salad 2";
? ? ? ? ? ? Toppings.Add("Grated Reggiano Cheese 2");
? ? ? ? }
? ? }
}
namespace FactoryMethodPattern.Pizzas
{
? ? public class NYClamPizza: Pizza
? ? {
? ? ? ? public NYClamPizza()
? ? ? ? {
? ? ? ? ? ? Name = "NY? Clam Pizza";
? ? ? ? ? ? Sauce = "Tomato sauce 2";
? ? ? ? ? ? Dough = "Soft dough 2";
? ? ? ? ? ? Toppings.Add("Shrimp meat 2");
? ? ? ? }
? ? }
}
namespace FactoryMethodPattern.Pizzas
{
? ? public class NYPepperoniPizza: Pizza
? ? {
? ? ? ? public NYPepperoniPizza()
? ? ? ? {
? ? ? ? ? ? Name = "NY Pepperoni Pizza";
? ? ? ? ? ? Dough = "Thin dough 2";
? ? ? ? ? ? Sauce = "Black pepper 2";
? ? ? ? ? ? Toppings.Add("Beef Granules 2");
? ? ? ? ? ? Toppings.Add("Niblet 2");
? ? ? ? }
? ? }
}
披薩店抽象父類:
using FactoryMethodPattern.Pizzas;
namespace FactoryMethodPattern
{
? ? 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);
? ? }
}
Chicago披薩店:
using FactoryMethodPattern.Pizzas;
namespace FactoryMethodPattern
{
? ? public class ChicagoPizzaStore: PizzaStore
? ? {
? ? ? ? protected override Pizza CreatePizza(string type)
? ? ? ? {
? ? ? ? ? ? Pizza pizza = null;
? ? ? ? ? ? switch (type)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? case "cheese":
? ? ? ? ? ? ? ? ? ? pizza = new ChicagoCheesePizza();
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? case "pepperoni":
? ? ? ? ? ? ? ? ? ? pizza = new ChicagoPepperoniPizza();
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? case "clam":
? ? ? ? ? ? ? ? ? ? pizza = new ChicagoClamPizza();
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? ? ? return pizza;
? ? ? ? }
? ? }
}
紐約披薩店:
using FactoryMethodPattern.Pizzas;
namespace FactoryMethodPattern
{
? ? public class NYPizzaStore : PizzaStore
? ? {
? ? ? ? protected override Pizza CreatePizza(string type)
? ? ? ? {
? ? ? ? ? ? Pizza pizza = null;
? ? ? ? ? ? switch (type)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? case "cheese":
? ? ? ? ? ? ? ? ? ? pizza = new NYCheesePizza();
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? case "pepperoni":
? ? ? ? ? ? ? ? ? ? pizza = new NYPepperoniPizza();
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? case "clam":
? ? ? ? ? ? ? ? ? ? pizza = new NYClamPizza();
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? ? ? return pizza;
? ? ? ? }
? ? }
}
測試運(yùn)行:
using System;
namespace FactoryMethodPattern
{
? ? class Program
? ? {
? ? ? ? static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? var nyStore = new NYPizzaStore();
? ? ? ? ? ? var chicagoStore = new ChicagoPizzaStore();
? ? ? ? ? ? var pizza = nyStore.OrderPizza("cheese");
? ? ? ? ? ? Console.WriteLine($"Ordered a {pizza.Name} in NY");
? ? ? ? ? ? Console.WriteLine();
? ? ? ? ? ? var pizza2 = chicagoStore.OrderPizza("cheese");
? ? ? ? ? ? Console.WriteLine($"Ordered a {pizza2.Name} in Chicago");
? ? ? ? ? ? Console.ReadKey();
? ? ? ? }
? ? }
}
?
相關(guān)文章:
C# ?觀察者模式 以及 delegate 和 event
原文地址 http://www.cnblogs.com/cgzl/p/8760250.html
.NET社區(qū)新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com
總結(jié)
以上是生活随笔為你收集整理的用C#(.NET Core) 实现简单工厂和工厂方法设计模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET Core 2.1 Previe
- 下一篇: 使用 C# (.NET Core) 实现