详解设计模式之工厂模式(简单工厂+工厂方法+抽象工厂)
園子里關(guān)于23種設(shè)計模式的博文已經(jīng)可以說是成千上萬、車載斗量、屯街塞巷、不計其數(shù)、數(shù)不勝數(shù)、摩肩接踵、汗牛充棟、車水馬龍、門庭若市、琳瑯滿目直至讓人眼花繚亂了。在這樣的大環(huán)境下之所以來寫設(shè)計模式類的博文,并不是像一些"非主流"的愛情觀那樣"寧缺毋濫"。 只是其一呢,因為相當于給自己做一個總結(jié),加深一下自己這方面的認識,因為掌握了和把它寫出來我感覺后者還可以對技能有一個提升,其二呢是因為最近公司有一個內(nèi)部的training需要講設(shè)計模式。
v寫在前面 在這里呢,需要向園子里所有寫過設(shè)計模式的前輩們和程杰老師致敬,在coding的道路上從當初剛畢業(yè)的懵懵懂懂到現(xiàn)在的XXXXX,一路上是你們給了我們coding啟迪。不矯情了,開始正事。(建議在正式認識設(shè)計模式之前,可以先參照我的上一篇博文學習一下設(shè)計模式的六大原則。) v簡單工廠模式1.介紹:?
簡單工廠模式是屬于創(chuàng)建型模式,又叫做靜態(tài)工廠方法(Static Factory Method)模式,但不屬于23種GOF設(shè)計模式之一。簡單工廠模式是由一個工廠對象決定創(chuàng)建出哪一種產(chǎn)品類的實例。簡單工廠模式是工廠模式家族中最簡單實用的模式,可以理解為是不同工廠模式的一個特殊實現(xiàn)。2.延伸:?
試想一下,當我們在coding的時候,在A類里面只要NEW了一個B類的對象,那么A類就會從某種程度上依賴B類。如果在后期需求發(fā)生變化或者是維護的時候,需要修改B類的時候,我們就需要打開源代碼修改所有與這個類有關(guān)的類了,做過重構(gòu)的朋友都知道,這樣的事情雖然無法完全避免,但確實是一件讓人心碎的事情。3.模擬場景:?
歐美主導的以賽車為主題的系列電影《速度與激情》系列相信大家都看過,里面的男主角(zhǔ jué,加個拼音,經(jīng)常聽到有人說什么主腳主腳的,雖然之前我也不確定是zhǔ jué還是主腳,但是我沒念過主腳,我在不確定的情況下我都是念男一號)范·迪塞爾在每一集里面做不同的事情都是開不同的車子,相信大家都覺得很酷吧。
人家酷也沒辦法,誰叫人家是大佬呢。這里我們試想一下,如果這是一套程序,我們該怎么設(shè)計?每次不同的畫面或者劇情范·迪塞爾都需要按照導演的安排開不一樣的車,去參加賽車需要開的是跑車,可能導演就會說下一場戲:范·迪塞爾下一場戲需要開跑車(參數(shù)),要去參加五環(huán)首屆跑車拉力賽,這時候場務(wù)(工廠類)接到導演的命令(跑車參數(shù))后需要從車庫開出一輛跑車(具體產(chǎn)品)交到范·迪塞爾手上讓他去準備五環(huán)首屆跑車拉力賽。這套程序的整個生命周期就算完成了。(什么?沒完成?難不成你還真想來個五環(huán)首屆跑車拉力賽了啊:)
根據(jù)導演不同的指令,開的車是不一樣的,但是車都是在車庫中存在的。車都屬于同一種抽象,車庫里所有的車都有自己的特征,這些特征就是條件。導演發(fā)出指令的時候,只要告訴場務(wù)特征,場務(wù)就知道提什么車。這就簡單工廠模式的典型案例。
4.簡單工廠UML類圖:?(UML圖是我用windows自帶的paint手工畫的,所以可能不是很專業(yè))
5.代碼演示:?
抽象產(chǎn)品類代碼:?
namespace CNBlogs.DesignPattern.Common {/// <summary>/// 抽象產(chǎn)品類: 汽車/// </summary>public interface ICar{void GetCar();} }具體產(chǎn)品類代碼:?
namespace CNBlogs.DesignPattern.Common {public enum CarType{SportCarType = 0,JeepCarType = 1,HatchbackCarType = 2}/// <summary>/// 具體產(chǎn)品類: 跑車/// </summary>public class SportCar : ICar{public void GetCar(){Console.WriteLine("場務(wù)把跑車交給范·迪塞爾");}}/// <summary>/// 具體產(chǎn)品類: 越野車/// </summary>public class JeepCar : ICar{public void GetCar(){Console.WriteLine("場務(wù)把越野車交給范·迪塞爾");}}/// <summary>/// 具體產(chǎn)品類: 兩箱車/// </summary>public class HatchbackCar : ICar{public void GetCar(){Console.WriteLine("場務(wù)把兩箱車交給范·迪塞爾");}} }簡單工廠核心代碼:?
namespace CNBlogs.DesignPattern.Common {public class Factory{public ICar GetCar(CarType carType){switch (carType){case CarType.SportCarType:return new SportCar();case CarType.JeepCarType:return new JeepCar();case CarType.HatchbackCarType:return new HatchbackCar();default:throw new Exception("愛上一匹野馬,可我的家里沒有草原. 你走吧!");}}} }客戶端調(diào)用代碼:?
//------------------------------------------------------------------------------ // <copyright file="Program.cs" company="CNBlogs Corporation"> // Copyright (C) 2015-2016 All Rights Reserved // 原博文地址: http://www.cnblogs.com/toutou/ // 作 者: 請叫我頭頭哥 // </copyright> //------------------------------------------------------------------------------ namespace CNBlogs.DesignPattern {using System;using CNBlogs.DesignPattern.Common;class Program{static void Main(string[] args){ICar car;try{Factory factory = new Factory();Console.WriteLine("范·迪塞爾下一場戲開跑車。");car = factory.GetCar(CarType.SportCarType);car.GetCar();}catch (Exception ex){Console.WriteLine(ex.Message);}}} }簡單工廠的簡單案例就這么多,真正在項目實戰(zhàn)的話可能還有需要改進和擴展的地方。因需求而定吧。
6.簡單工廠的優(yōu)點/缺點:?
- 優(yōu)點:簡單工廠模式能夠根據(jù)外界給定的信息,決定究竟應(yīng)該創(chuàng)建哪個具體類的對象。明確區(qū)分了各自的職責和權(quán)力,有利于整個軟件體系結(jié)構(gòu)的優(yōu)化。
- 缺點:很明顯工廠類集中了所有實例的創(chuàng)建邏輯,容易違反GRASPR的高內(nèi)聚的責任分配原則
1.介紹:?
工廠方法模式Factory Method,又稱多態(tài)性工廠模式。在工廠方法模式中,核心的工廠類不再負責所有的產(chǎn)品的創(chuàng)建,而是將具體創(chuàng)建的工作交給子類去做。該核心類成為一個抽象工廠角色,僅負責給出具體工廠子類必須實現(xiàn)的接口,而不接觸哪一個產(chǎn)品類應(yīng)當被實例化這種細節(jié)。2.定義:?
工廠方法模式是簡單工廠模式的衍生,解決了許多簡單工廠模式的問題。首先完全實現(xiàn)‘開-閉 原則’,實現(xiàn)了可擴展。其次更復雜的層次結(jié)構(gòu),可以應(yīng)用于產(chǎn)品結(jié)果復雜的場合。3.延伸:?
在上面簡單工廠的引入中,我們將實例化具體對象的工作全部交給了專門負責創(chuàng)建對象的工廠類(場務(wù))中,這樣就可以在我們得到導演的命令后創(chuàng)建對應(yīng)的車(產(chǎn)品)類了。但是劇組的導演是性情比較古怪的,可能指令也是無限變化的。這樣就有了新的問題,一旦導演發(fā)出的指令時我們沒有預(yù)料到的,就必須得修改源代碼。這也不是很合理的。工廠方法就是為了解決這類問題的。4.模擬場景:?
還是上面范·迪塞爾要去參加五環(huán)首屆跑車拉力賽的場景。因為要拍攝《速度與激情8》,導演組車的種類增多了,陣容也更加豪華了,加上導演古怪的性格可能每一場戲絕對需要試駕幾十種車。如果車庫沒有的車(具體產(chǎn)品類)可以由場務(wù)(具體工廠類)直接去4S店取,這樣沒增加一種車(具體產(chǎn)品類)就要對應(yīng)的有一個場務(wù)(具體工廠類),他們互相之間有著各自的職責,互不影響,這樣可擴展性就變強了。5.工廠方法UML類圖:?(UML圖是我用windows自帶的paint手工畫的,所以可能不是很專業(yè)
6.代碼演示:?
抽象工廠代碼:?
namespace CNBlogs.DesignPattern.Common {public interface IFactory{ICar CreateCar();} }抽象產(chǎn)品代碼:?
namespace CNBlogs.DesignPattern.Common {public interface ICar{void GetCar();} }具體工廠代碼:?
namespace CNBlogs.DesignPattern.Common {/// <summary>/// 具體工廠類: 用于創(chuàng)建跑車類/// </summary>public class SportFactory : IFactory{public ICar CreateCar(){return new SportCar();}}/// <summary>/// 具體工廠類: 用于創(chuàng)建越野車類/// </summary>public class JeepFactory : IFactory{public ICar CreateCar(){return new JeepCar();}}/// <summary>/// 具體工廠類: 用于創(chuàng)建兩廂車類/// </summary>public class HatchbackFactory : IFactory{public ICar CreateCar(){return new HatchbackCar();}} }具體產(chǎn)品代碼:?
namespace CNBlogs.DesignPattern.Common {/// <summary>/// 具體產(chǎn)品類: 跑車/// </summary>public class SportCar : ICar{public void GetCar(){Console.WriteLine("場務(wù)把跑車交給范·迪塞爾");}}/// <summary>/// 具體產(chǎn)品類: 越野車/// </summary>public class JeepCar : ICar{public void GetCar(){Console.WriteLine("場務(wù)把越野車交給范·迪塞爾");}}/// <summary>/// 具體產(chǎn)品類: 兩箱車/// </summary>public class HatchbackCar : ICar{public void GetCar(){Console.WriteLine("場務(wù)把兩箱車交給范·迪塞爾");}} }客戶端代碼:?
//------------------------------------------------------------------------------ // <copyright file="Program.cs" company="CNBlogs Corporation"> // Copyright (C) 2015-2016 All Rights Reserved // 原博文地址: http://www.cnblogs.com/toutou/ // 作 者: 請叫我頭頭哥 // </copyright> //------------------------------------------------------------------------------ namespace CNBlogs.DesignPattern {using System.IO;using System.Configuration;using System.Reflection;using CNBlogs.DesignPattern.Common;class Program{static void Main(string[] args){// 工廠類的類名寫在配置文件中可以方便以后修改string factoryType = ConfigurationManager.AppSettings["FactoryType"];// 這里把DLL配置在數(shù)據(jù)庫是因為以后數(shù)據(jù)可能發(fā)生改變// 比如說現(xiàn)在的數(shù)據(jù)是從sql server取的,以后需要從oracle取的話只需要添加一個訪問oracle數(shù)據(jù)庫的工程就行了string dllName = ConfigurationManager.AppSettings["DllName"];// 利用.NET提供的反射可以根據(jù)類名來創(chuàng)建它的實例,非常方便var currentAssembly = System.Reflection.Assembly.GetExecutingAssembly();string codeBase = currentAssembly.CodeBase.ToLower().Replace(currentAssembly.ManifestModule.Name.ToLower(), string.Empty);IFactory factory = Assembly.LoadFrom(Path.Combine(codeBase, dllName)).CreateInstance(factoryType) as IFactory;ICar car = factory.CreateCar();car.GetCar();}} }7.工廠方法的優(yōu)點/缺點:?
- 優(yōu)點:
- 子類提供掛鉤?;悶楣S方法提供缺省實現(xiàn),子類可以重寫新的實現(xiàn),也可以繼承父類的實現(xiàn)。-- 加一層間接性,增加了靈活性
- 屏蔽產(chǎn)品類。產(chǎn)品類的實現(xiàn)如何變化,調(diào)用者都不需要關(guān)心,只需關(guān)心產(chǎn)品的接口,只要接口保持不變,系統(tǒng)中的上層模塊就不會發(fā)生變化。
- 典型的解耦框架。高層模塊只需要知道產(chǎn)品的抽象類,其他的實現(xiàn)類都不需要關(guān)心,符合迪米特法則,符合依賴倒置原則,符合里氏替換原則。
- 多態(tài)性:客戶代碼可以做到與特定應(yīng)用無關(guān),適用于任何實體類。
- 缺點:需要Creator和相應(yīng)的子類作為factory method的載體,如果應(yīng)用模型確實需要creator和子類存在,則很好;否則的話,需要增加一個類層次。(不過說這個缺點好像有點吹毛求疵了)
1.介紹:?
抽象工廠模式是所有形態(tài)的工廠模式中最為抽象和最具一般性的一種形態(tài)。抽象工廠模式是指當有多個抽象角色時,使用的一種工廠模式。抽象工廠模式可以向客戶端提供一個接口,使客戶端在不必指定產(chǎn)品的具體的情況下,創(chuàng)建多個產(chǎn)品族中的產(chǎn)品對象。根據(jù)里氏替換原則,任何接受父類型的地方,都應(yīng)當能夠接受子類型。因此,實際上系統(tǒng)所需要的,僅僅是類型與這些抽象產(chǎn)品角色相同的一些實例,而不是這些抽象產(chǎn)品的實例。換言之,也就是這些抽象產(chǎn)品的具體子類的實例。工廠類負責創(chuàng)建抽象產(chǎn)品的具體子類的實例。2.定義:?
為創(chuàng)建一組相關(guān)或相互依賴的對象提供一個接口,而且無需指定他們的具體類。3.模擬場景:?
我們還是繼續(xù)范·迪塞爾的例子,往往這些大牌生活中經(jīng)常參加一些活動,或是商務(wù)活動或是公益活動。不管參加什么活動,加上老范(范·迪塞爾名字太長,以下文中簡稱老范)的知名度,他的車肯定不少,可能光跑車或者光越野車就有多輛。比如說有跑車(多輛,跑車系列的具體產(chǎn)品)、越野車(多輛,越野車系列的具體產(chǎn)品)、兩箱車(多輛,兩箱車系列的具體產(chǎn)品)??赡芎芏啻笈泼餍嵌际侨绱说?。假設(shè)老范家里,某一個車庫(具體工廠)只存放某一系列的車(比如說跑車車庫只存放跑車一系列具體的產(chǎn)品),每次要某一輛跑車的時候肯定要從這個跑車車庫里開出來。用了OO(Object Oriented,面向?qū)ο?的思想去理解,所有的車庫(具體工廠)都是車庫類(抽象工廠)的某一個,而每一輛車又包括具體的開車時候所背的包(某一具體產(chǎn)品。包是也是放在車庫里的,不同的車搭配不同的包,我們把車和車對應(yīng)的背包稱作出去參加活動的裝備),這些具體的包其實也都是背包(抽象產(chǎn)品),具體的車其實也都是車(另一個抽象產(chǎn)品)。4.場景分析:?
上面的場景可能有點稀里糊涂的,但是用OO的思想結(jié)合前面的簡單工廠和工廠方法的思路去理解的話,也好理解。
下面讓我們來捋一捋這個思路:
- 抽象工廠:虛擬的車庫,只是所有車庫的一個概念。在程序中可能是一個借口或者抽象類,對其他車庫的規(guī)范,開車和取包。
- 具體工廠:具體存在的車庫,用來存放車和車對應(yīng)的背包。在程序中繼承抽象工廠,實現(xiàn)抽象工廠中的方法,可以有具體的產(chǎn)品。
- 抽象產(chǎn)品:虛擬的裝備(車和對應(yīng)的背包),也只是所有裝備的一個概念。在程序中可能是多個接口或者多個抽象類,對具體的裝備起到規(guī)范。
- 具體產(chǎn)品:活動參加的具體裝備,它指的是組成裝備的某一輛車或者背包。它繼承自某一個抽象產(chǎn)品。
5.抽象工廠UML類圖:?(UML圖是我用windows自帶的paint手工畫的,所以可能不是很專業(yè))
6.代碼演示:?
抽象工廠代碼:?
namespace CNBlogs.DesignPattern.Common {/// <summary>/// 抽象工廠類/// </summary>public abstract class AbstractEquipment{/// <summary>/// 抽象方法: 創(chuàng)建一輛車/// </summary>/// <returns></returns>public abstract AbstractCar CreateCar();/// <summary>/// 抽象方法: 創(chuàng)建背包/// </summary>/// <returns></returns>public abstract AbstractBackpack CreateBackpack();} }抽象產(chǎn)品代碼:?
namespace CNBlogs.DesignPattern.Common {/// <summary>/// 抽象產(chǎn)品: 車抽象類/// </summary>public abstract class AbstractCar{/// <summary>/// 車的類型屬性/// </summary>public abstract string Type{get;}/// <summary>/// 車的顏色屬性/// </summary>public abstract string Color{get;}}/// <summary>/// 抽象產(chǎn)品: 背包抽象類/// </summary>public abstract class AbstractBackpack{/// <summary>/// 包的類型屬性/// </summary>public abstract string Type{get;}/// <summary>/// 包的顏色屬性/// </summary>public abstract string Color{get;}} }具體工廠代碼:?
namespace CNBlogs.DesignPattern.Common {/// <summary>/// 運動裝備/// </summary>public class SportEquipment : AbstractEquipment{public override AbstractCar CreateCar(){return new SportCar();}public override AbstractBackpack CreateBackpack(){return new SportBackpack();}}/// <summary>/// 越野裝備 這里就不添加了,同運動裝備一個原理,demo里只演示一個,實際項目中可以按需添加/// </summary>//public class JeepEquipment : AbstractEquipment//{// public override AbstractCar CreateCar()// {// return new JeeptCar();// }// public override AbstractBackpack CreateBackpack()// {// return new JeepBackpack();// }//} }具體產(chǎn)品代碼:?
namespace CNBlogs.DesignPattern.Common {/// <summary>/// 跑車/// </summary>public class SportCar : AbstractCar{private string type = "Sport";private string color = "Red";/// <summary>/// 重寫基類的Type屬性/// </summary>public override string Type{get{return type;}}/// <summary>/// 重寫基類的Color屬性/// </summary>public override string Color{get{return color;}}}/// <summary>/// 運動背包/// </summary>public class SportBackpack : AbstractBackpack{private string type = "Sport";private string color = "Red";/// <summary>/// 重寫基類的Type屬性/// </summary>public override string Type{get{return type;}}/// <summary>/// 重寫基類的Color屬性/// </summary>public override string Color{get{return color;}}} } //具體產(chǎn)品可以有很多很多, 至于越野類的具體產(chǎn)品這里就不列出來了。創(chuàng)建裝備代碼:?
namespace CNBlogs.DesignPattern.Common {public class CreateEquipment{private AbstractCar fanCar;private AbstractBackpack fanBackpack;public CreateEquipment(AbstractEquipment equipment){fanCar = equipment.CreateCar();fanBackpack = equipment.CreateBackpack();}public void ReadyEquipment(){Console.WriteLine(string.Format("老范背著{0}色{1}包開著{2}色{3}車。", fanBackpack.Color, fanBackpack.Type,fanCar.Color,fanCar.Type));}} }客戶端代碼:?
//------------------------------------------------------------------------------ // <copyright file="Program.cs" company="CNBlogs Corporation"> // Copyright (C) 2015-2016 All Rights Reserved // 原博文地址: http://www.cnblogs.com/toutou/ // 作 者: 請叫我頭頭哥 // </copyright> //------------------------------------------------------------------------------ namespace CNBlogs.DesignPattern {using System;using System.Configuration;using System.Reflection;using CNBlogs.DesignPattern.Common;class Program{static void Main(string[] args){// ***具體app.config配置如下*** ////<add key="assemblyName" value="CNBlogs.DesignPattern.Common"/>//<add key="nameSpaceName" value="CNBlogs.DesignPattern.Common"/>//<add key="typename" value="SportEquipment"/>// 創(chuàng)建一個工廠類的實例string assemblyName = ConfigurationManager.AppSettings["assemblyName"];string fullTypeName = string.Concat(ConfigurationManager.AppSettings["nameSpaceName"], ".", ConfigurationManager.AppSettings["typename"]);AbstractEquipment factory = (AbstractEquipment)Assembly.Load(assemblyName).CreateInstance(fullTypeName);CreateEquipment equipment = new CreateEquipment(factory);equipment.ReadyEquipment();Console.Read();}} }抽象工廠模式符合了六大原則中的開閉原則、里氏代換原則、依賴倒轉(zhuǎn)原則等等
7.抽象工廠的優(yōu)點/缺點:?
- 優(yōu)點:
- 抽象工廠模式隔離了具體類的生產(chǎn),使得客戶并不需要知道什么被創(chuàng)建。
- 當一個產(chǎn)品族中的多個對象被設(shè)計成一起工作時,它能保證客戶端始終只使用同一個產(chǎn)品族中的對象。
- 增加新的具體工廠和產(chǎn)品族很方便,無須修改已有系統(tǒng),符合“開閉原則”。
- 缺點:增加新的產(chǎn)品等級結(jié)構(gòu)很復雜,需要修改抽象工廠和所有的具體工廠類,對“開閉原則”的支持呈現(xiàn)傾斜性。(不過說這個缺點好像有點吹毛求疵了)
這篇博文從晚上下班7點到家一直寫到現(xiàn)在,說了一晚上的工廠,也扯了一晚上的速度與激情,在本博文完結(jié)的最后,給大家來一張速度與激情的畫面精彩照。(ps:是不是覺得這種畫面再配上一曲DJ一瓶啤酒會更嗨啊?哈哈...)
我們使用設(shè)計模式目的無非只有三個:a)縮短開發(fā)時間;b)降低維護成本;c)在應(yīng)用程序之間和內(nèi)部輕松集成。具體什么時候使用何種設(shè)計模式還得因項目而異。之所以對設(shè)計模式舊調(diào)重彈只是希望這個博文能對自己的架構(gòu)之路有所提升,同時如果能幫助到其他人那就更完美了。
?
作 者:請叫我頭頭哥
出 處:http://www.cnblogs.com/toutou/
關(guān)于作者:專注于基礎(chǔ)平臺的項目開發(fā)。如有問題或建議,請多多賜教!
版權(quán)聲明:本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
特此聲明:所有評論和私信都會在第一時間回復。也歡迎園子的大大們指正錯誤,共同進步?;蛘咧苯铀叫盼?
聲援博主:如果您覺得文章對您有幫助,可以點擊文章右下角【推薦】一下。您的鼓勵是作者堅持原創(chuàng)和持續(xù)寫作的最大動力!
總結(jié)
以上是生活随笔為你收集整理的详解设计模式之工厂模式(简单工厂+工厂方法+抽象工厂)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android Studio: fini
- 下一篇: 学习XML(添加一个子节点) 摘录