程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 詳解設計模式之工廠模式(簡單工廠+工廠方法+抽象工廠),詳解設計模式

詳解設計模式之工廠模式(簡單工廠+工廠方法+抽象工廠),詳解設計模式

編輯:C#入門知識

詳解設計模式之工廠模式(簡單工廠+工廠方法+抽象工廠),詳解設計模式


園子裡關於23種設計模式的博文已經可以說是成千上萬、車載斗量、屯街塞巷、不計其數、數不勝數、摩肩接踵、汗牛充棟、車水馬龍、門庭若市、琳琅滿目直至讓人眼花缭亂了。在這樣的大環境下之所以來寫設計模式類的博文,並不是像一些"非主流"的愛情觀那樣"寧缺毋濫"。 只是其一呢,因為相當於給自己做一個總結,加深一下自己這方面的認識,因為掌握了和把它寫出來我感覺後者還可以對技能有一個提升,其二呢是因為最近公司有一個內部的training需要講設計模式。

C# 設計模式 v寫在前面 在這裡呢,需要向園子裡所有寫過設計模式的前輩們和程傑老師致敬,在codeing的道路上從當初剛畢業的懵懵懂懂到現在的XXXXX,一路上是你們給了我們codeing啟迪。不矯情了,開始正事。(建議在正式認識設計模式之前,可以先參照我的上一篇博文學習一下設計模式的六大原則。) v簡單工廠模式

1.介紹: 

簡單工廠模式是屬於創建型模式,又叫做靜態工廠方法(Static Factory Method)模式,但不屬於23種GOF設計模式之一。簡單工廠模式是由一個工廠對象決定創建出哪一種產品類的實例。簡單工廠模式是工廠模式家族中最簡單實用的模式,可以理解為是不同工廠模式的一個特殊實現。

2.延伸: 

試想一下,當我們在codeing的時候,在A類裡面只要NEW了一個B類的對象,那麼A類就會從某種程度上依賴B類。如果在後期需求發生變化或者是維護的時候,需要修改B類的時候,我們就需要打開源代碼修改所有與這個類有關的類了,做過重構的朋友都知道,這樣的事情雖然無法完全避免,但確實是一件讓人心碎的事情。

3.模擬場景: 

歐美主導的以賽車為主題的系列電影《速度與激情》系列相信大家都看過,裡面的男主角(zhǔ jué,加個拼音,經常聽到有人說什麼主腳主腳的,雖然之前我也不確定是zhǔ jué還是主腳,但是我沒念過主腳,我在不確定的情況下我都是念男一號)范·迪塞爾在每一集裡面做不同的事情都是開不同的車子,相信大家都覺得很酷吧。

人家酷也沒辦法,誰叫人家是大佬呢。這裡我們試想一下,如果這是一套程序,我們該怎麼設計?每次不同的畫面或者劇情范·迪塞爾都需要按照導演的安排開不一樣的車,去參加賽車需要開的是跑車,可能導演就會說下一場戲:范·迪塞爾下一場戲需要開跑車(參數),要去參加五環首屆跑車拉力賽,這時候場務(工廠類)接到導演的命令(跑車參數)後需要從車庫開出一輛跑車(具體產品)交到范·迪塞爾手上讓他去准備五環首屆跑車拉力賽。這套程序的整個生命周期就算完成了。(什麼?沒完成?難不成你還真想來個五環首屆跑車拉力賽了啊:)

根據導演不同的指令,開的車是不一樣的,但是車都是在車庫中存在的。車都屬於同一種抽象,車庫裡所有的車都有自己的特征,這些特征就是條件。導演發出指令的時候,只要告訴場務特征,場務就知道提什麼車。這就簡單工廠模式的典型案例。

4.簡單工廠UML類圖: (UML圖是我用windows自帶的paint手工畫的,所以可能不是很專業)

C# 簡單工廠模式

5.代碼演示: 

抽象產品類代碼: 

namespace CNBlogs.DesignPattern.Common
{
    /// <summary>
    /// 抽象產品類: 汽車
    /// </summary>
    public interface ICar
    {
        void GetCar();
    }
}

具體產品類代碼: 

namespace CNBlogs.DesignPattern.Common
{
    public enum CarType
    {
        SportCarType = 0,
        JeepCarType = 1,
        HatchbackCarType = 2
    }

    /// <summary>
    /// 具體產品類: 跑車
    /// </summary>
    public class SportCar : ICar
    {
        public void GetCar()
        {
            Console.WriteLine("場務把跑車交給范·迪塞爾");
        }
    }

    /// <summary>
    /// 具體產品類: 越野車
    /// </summary>
    public class JeepCar : ICar
    {
        public void GetCar()
        {
            Console.WriteLine("場務把越野車交給范·迪塞爾");
        }
    }

    /// <summary>
    /// 具體產品類: 兩箱車
    /// </summary>
    public class HatchbackCar : ICar
    {
        public void GetCar()
        {
            Console.WriteLine("場務把兩箱車交給范·迪塞爾");
        }
    }
}

簡單工廠核心代碼: 

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("愛上一匹野馬,可我的家裡沒有草原. 你走吧!");
            }
        }
    }
}

客戶端調用代碼: 

//------------------------------------------------------------------------------
// <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);
            }
        }
    }
}

簡單工廠的簡單案例就這麼多,真正在項目實戰的話可能還有需要改進和擴展的地方。因需求而定吧。

6.簡單工廠的優點/缺點: 

  • 優點:簡單工廠模式能夠根據外界給定的信息,決定究竟應該創建哪個具體類的對象。明確區分了各自的職責和權力,有利於整個軟件體系結構的優化。
  • 缺點:很明顯工廠類集中了所有實例的創建邏輯,容易違反GRASPR的高內聚的責任分配原則
v工廠方法模式

1.介紹: 

工廠方法模式Factory Method,又稱多態性工廠模式。在工廠方法模式中,核心的工廠類不再負責所有的產品的創建,而是將具體創建的工作交給子類去做。該核心類成為一個抽象工廠角色,僅負責給出具體工廠子類必須實現的接口,而不接觸哪一個產品類應當被實例化這種細節。

2.定義: 

工廠方法模式是簡單工廠模式的衍生,解決了許多簡單工廠模式的問題。首先完全實現‘開-閉 原則’,實現了可擴展。其次更復雜的層次結構,可以應用於產品結果復雜的場合。

3.延伸: 

在上面簡單工廠的引入中,我們將實例化具體對象的工作全部交給了專門負責創建對象的工廠類(場務)中,這樣就可以在我們得到導演的命令後創建對應的車(產品)類了。但是劇組的導演是性情比較古怪的,可能指令也是無限變化的。這樣就有了新的問題,一旦導演發出的指令時我們沒有預料到的,就必須得修改源代碼。這也不是很合理的。工廠方法就是為了解決這類問題的。

4.模擬場景: 

還是上面范·迪塞爾要去參加五環首屆跑車拉力賽的場景。因為要拍攝《速度與激情8》,導演組車的種類增多了,陣容也更加豪華了,加上導演古怪的性格可能每一場戲絕對需要試駕幾十種車。如果車庫沒有的車(具體產品類)可以由場務(具體工廠類)直接去4S店取,這樣沒增加一種車(具體產品類)就要對應的有一個場務(具體工廠類),他們互相之間有著各自的職責,互不影響,這樣可擴展性就變強了。

5.工廠方法UML類圖: (UML圖是我用windows自帶的paint手工畫的,所以可能不是很專業

6.代碼演示: 

抽象工廠代碼: 

namespace CNBlogs.DesignPattern.Common
{
    public interface IFactory
    {
        ICar CreateCar();
    }
}

抽象產品代碼: 

namespace CNBlogs.DesignPattern.Common
{
    public interface ICar
    {
        void GetCar();
    }
}

具體工廠代碼: 

namespace CNBlogs.DesignPattern.Common
{
    /// <summary>
    ///  具體工廠類: 用於創建跑車類
    /// </summary>
    public class SportFactory : IFactory
    {
        public ICar CreateCar()
        {
            return new SportCar();
        }
    }

    /// <summary>
    ///  具體工廠類: 用於創建越野車類
    /// </summary>
    public class JeepFactory : IFactory
    {
        public ICar CreateCar()
        {
            return new JeepCar();
        }
    }

    /// <summary>
    ///  具體工廠類: 用於創建兩廂車類
    /// </summary>
    public class HatchbackFactory : IFactory
    {
        public ICar CreateCar()
        {
            return new HatchbackCar();
        }
    }
}

具體產品代碼: 

namespace CNBlogs.DesignPattern.Common
{
    /// <summary>
    /// 具體產品類: 跑車
    /// </summary>
    public class SportCar : ICar
    {
        public void GetCar()
        {
            Console.WriteLine("場務把跑車交給范·迪塞爾");
        }
    }

    /// <summary>
    /// 具體產品類: 越野車
    /// </summary>
    public class JeepCar : ICar
    {
        public void GetCar()
        {
            Console.WriteLine("場務把越野車交給范·迪塞爾");
        }
    }

    /// <summary>
    /// 具體產品類: 兩箱車
    /// </summary>
    public class HatchbackCar : ICar
    {
        public void GetCar()
        {
            Console.WriteLine("場務把兩箱車交給范·迪塞爾");
        }
    }
}

客戶端代碼: 

//------------------------------------------------------------------------------
// <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配置在數據庫是因為以後數據可能發生改變
            // 比如說現在的數據是從sql server取的,以後需要從oracle取的話只需要添加一個訪問oracle數據庫的工程就行了
            string dllName = ConfigurationManager.AppSettings["DllName"];

            // 利用.NET提供的反射可以根據類名來創建它的實例,非常方便
            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.工廠方法的優點/缺點: 

  • 優點:
    • 子類提供掛鉤。基類為工廠方法提供缺省實現,子類可以重寫新的實現,也可以繼承父類的實現。-- 加一層間接性,增加了靈活性
    • 屏蔽產品類。產品類的實現如何變化,調用者都不需要關心,只需關心產品的接口,只要接口保持不變,系統中的上層模塊就不會發生變化。
    • 典型的解耦框架。高層模塊只需要知道產品的抽象類,其他的實現類都不需要關心,符合迪米特法則,符合依賴倒置原則,符合裡氏替換原則。
    • 多態性:客戶代碼可以做到與特定應用無關,適用於任何實體類。
  • 缺點:需要Creator和相應的子類作為factory method的載體,如果應用模型確實需要creator和子類存在,則很好;否則的話,需要增加一個類層次。(不過說這個缺點好像有點吹毛求疵了)
v抽象工廠模式

1.介紹: 

抽象工廠模式是所有形態的工廠模式中最為抽象和最具一般性的一種形態。抽象工廠模式是指當有多個抽象角色時,使用的一種工廠模式。抽象工廠模式可以向客戶端提供一個接口,使客戶端在不必指定產品的具體的情況下,創建多個產品族中的產品對象。根據裡氏替換原則,任何接受父類型的地方,都應當能夠接受子類型。因此,實際上系統所需要的,僅僅是類型與這些抽象產品角色相同的一些實例,而不是這些抽象產品的實例。換言之,也就是這些抽象產品的具體子類的實例。工廠類負責創建抽象產品的具體子類的實例。

2.定義: 

為創建一組相關或相互依賴的對象提供一個接口,而且無需指定他們的具體類。

3.模擬場景: 

我們還是繼續范·迪塞爾的例子,往往這些大牌生活中經常參加一些活動,或是商務活動或是公益活動。不管參加什麼活動,加上老范(范·迪塞爾名字太長,以下文中簡稱老范)的知名度,他的車肯定不少,可能光跑車或者光越野車就有多輛。比如說有跑車(多輛,跑車系列的具體產品)、越野車(多輛,越野車系列的具體產品)、兩箱車(多輛,兩箱車系列的具體產品)。可能很多大牌明星都是如此的。假設老范家裡,某一個車庫(具體工廠)只存放某一系列的車(比如說跑車車庫只存放跑車一系列具體的產品),每次要某一輛跑車的時候肯定要從這個跑車車庫裡開出來。用了OO(Object Oriented,面向對象)的思想去理解,所有的車庫(具體工廠)都是車庫類(抽象工廠)的某一個,而每一輛車又包括具體的開車時候所背的包(某一具體產品。包是也是放在車庫裡的,不同的車搭配不同的包,我們把車和車對應的背包稱作出去參加活動的裝備),這些具體的包其實也都是背包(抽象產品),具體的車其實也都是車(另一個抽象產品)。

4.場景分析: 

上面的場景可能有點稀裡糊塗的,但是用OO的思想結合前面的簡單工廠和工廠方法的思路去理解的話,也好理解。

下面讓我們來捋一捋這個思路:

  • 抽象工廠:虛擬的車庫,只是所有車庫的一個概念。在程序中可能是一個借口或者抽象類,對其他車庫的規范,開車和取包。
  • 具體工廠:具體存在的車庫,用來存放車和車對應的背包。在程序中繼承抽象工廠,實現抽象工廠中的方法,可以有具體的產品。
  • 抽象產品:虛擬的裝備(車和對應的背包),也只是所有裝備的一個概念。在程序中可能是多個接口或者多個抽象類,對具體的裝備起到規范。
  • 具體產品:活動參加的具體裝備,它指的是組成裝備的某一輛車或者背包。它繼承自某一個抽象產品。

5.抽象工廠UML類圖: (UML圖是我用windows自帶的paint手工畫的,所以可能不是很專業)

6.代碼演示: 

抽象工廠代碼: 

namespace CNBlogs.DesignPattern.Common
{
    /// <summary>
    /// 抽象工廠類
    /// </summary>
    public abstract class AbstractEquipment
    {
        /// <summary>
        /// 抽象方法: 創建一輛車
        /// </summary>
        /// <returns></returns>
        public abstract AbstractCar CreateCar();

        /// <summary>
        /// 抽象方法: 創建背包
        /// </summary>
        /// <returns></returns>
        public abstract AbstractBackpack CreateBackpack();
    }
}

抽象產品代碼: 

namespace CNBlogs.DesignPattern.Common
{
    /// <summary>
    /// 抽象產品: 車抽象類
    /// </summary>
    public abstract class AbstractCar
    {
        /// <summary>
        /// 車的類型屬性
        /// </summary>
        public abstract string Type
        {
            get;
        }

        /// <summary>
        /// 車的顏色屬性
        /// </summary>
        public abstract string Color
        {
            get;
        }
    }

    /// <summary>
    /// 抽象產品: 背包抽象類
    /// </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();
    //    }
    //}
}

具體產品代碼: 

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;
            }
        }
    }
}
//具體產品可以有很多很多, 至於越野類的具體產品這裡就不列出來了。

創建裝備代碼: 

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"/>
            // 創建一個工廠類的實例
            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();
        }
    }
}

抽象工廠模式符合了六大原則中的開閉原則、裡氏代換原則、依賴倒轉原則等等

7.抽象工廠的優點/缺點: 

  • 優點:
    • 抽象工廠模式隔離了具體類的生產,使得客戶並不需要知道什麼被創建。
    • 當一個產品族中的多個對象被設計成一起工作時,它能保證客戶端始終只使用同一個產品族中的對象。
    • 增加新的具體工廠和產品族很方便,無須修改已有系統,符合“開閉原則”。
  • 缺點:增加新的產品等級結構很復雜,需要修改抽象工廠和所有的具體工廠類,對“開閉原則”的支持呈現傾斜性。(不過說這個缺點好像有點吹毛求疵了)
v博客總結

這篇博文從晚上下班7點到家一直寫到現在,說了一晚上的工廠,也扯了一晚上的速度與激情,在本博文完結的最後,給大家來一張速度與激情的畫面精彩照。(ps:是不是覺得這種畫面再配上一曲DJ一瓶啤酒會更嗨啊?哈哈...)

我們使用設計模式目的無非只有三個:a)縮短開發時間;b)降低維護成本;c)在應用程序之間和內部輕松集成。具體什麼時候使用何種設計模式還得因項目而異。之所以對設計模式舊調重彈只是希望這個博文能對自己的架構之路有所提升,同時如果能幫助到其他人那就更完美了。

 


作  者:請叫我頭頭哥
出  處:http://www.cnblogs.com/toutou/
關於作者:專注於微軟平台的項目開發。如有問題或建議,請多多賜教!
版權聲明:本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
特此聲明:所有評論和私信都會在第一時間回復。也歡迎園子的大大們指正錯誤,共同進步。或者直接私信我
聲援博主:如果您覺得文章對您有幫助,可以點擊文章右下角【推薦】一下。您的鼓勵是作者堅持原創和持續寫作的最大動力!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved