聊聊工廠模式

昨天寫代碼的時(shí)候發(fā)現(xiàn)大多代碼都一樣袋马,只有類型不一樣逮诲,當(dāng)時(shí)腦海里就冒出“工廠模式”的概念宜肉,但又說(shuō)不清到底什么是工廠模式,我所遇到的情況又到底適不適合使用工廠模式侦啸,于是花時(shí)間好好把工廠模式看了一下槽唾,想通過(guò)這篇文章來(lái)輸出我所看到和get到的內(nèi)容丧枪。

我比較喜歡旅游,就拿旅游產(chǎn)品來(lái)舉例子吧庞萍。

簡(jiǎn)單工廠

首先抽象一個(gè)旅游產(chǎn)品接口拧烦,該接口有一個(gè)獲取線路列表的行為:

    /// <summary>
    /// 旅游產(chǎn)品接口
    /// </summary>
    public interface ITravelProduct
    {
        /// <summary>
        /// 獲取旅游產(chǎn)品線路列表
        /// </summary>
        /// <returns>The line list.</returns>
        string GetLineList();
    }

旅游產(chǎn)品有很多種,比如國(guó)內(nèi)游(具體的產(chǎn)品類):

    /// <summary>
    /// 國(guó)內(nèi)游
    /// </summary>
    public class DomesticTravel:ITravelProduct
    {
        public string GetLineList() => "您獲取到國(guó)內(nèi)游的線路列表啦挂绰!";
    }

再比如出境游(具體的產(chǎn)品類):

    /// <summary>
    /// 出境游
    /// </summary>
    public class OutboundTravel:ITravelProduct
    {
        public string GetLineList() => "您獲取到出境游的線路列表啦屎篱!";
    }

再比如郵輪游(具體的產(chǎn)品類):

    /// <summary>
    /// 郵輪游
    /// </summary>
    public class CruiseTravel:ITravelProduct
    {
        public string GetLineList() => "您獲取到郵輪游的線路列表啦!";
    }

然后我們來(lái)到一家OTA葵蒂,看到網(wǎng)站上提供了如下幾種旅游產(chǎn)品的線路列表(簡(jiǎn)單工廠類):

    /// <summary>
    /// 簡(jiǎn)單工廠模式的工廠類
    /// </summary>
    public class SimpleTravelProductFactory
    {
        public const int Domestic = 1;//國(guó)內(nèi)游
        public const int Outbond = 2;//出境游
        public const int Cruise = 3;//郵輪

        public static ITravelProduct createTravelProduct(int type){
            switch(type){
                case Domestic:
                    return new DomesticTravel();
                case Outbond:
                    return new OutboundTravel();
                case Cruise:
                    return new CruiseTravel();
                default:
                    return new DomesticTravel();

            }
        }
    }

下面就看你想選擇哪種旅游產(chǎn)品啦交播,比如你想選個(gè)國(guó)內(nèi)游的線路列表看看,那么践付,我們就讓簡(jiǎn)單工廠給我們返回一下國(guó)內(nèi)游的線路列表:

        public static void Main(string[] args)
        {
            Console.WriteLine("=========簡(jiǎn)單工廠模式==========");
            string domesticLineList = SimpleTravelProductFactory.createTravelProduct(SimpleTravelProductFactory.Domestic).GetLineList();
            Console.WriteLine(domesticLineList);
        }

輸出結(jié)果:

=========簡(jiǎn)單工廠模式==========
您獲取到國(guó)內(nèi)游的線路列表啦秦士!

如果你想獲取出境游的線路列表活著郵輪游的線路列表,只需要在創(chuàng)建旅游產(chǎn)品時(shí)傳入對(duì)應(yīng)的產(chǎn)品類型即可永高。
顯而易見(jiàn)隧土,簡(jiǎn)單工廠的缺點(diǎn)就是,如果有很多很多種產(chǎn)品類型命爬,那么你工廠類中的switch曹傀,case語(yǔ)句會(huì)很長(zhǎng),而且在做產(chǎn)品的擴(kuò)展時(shí)饲宛,除了要增加具體的產(chǎn)品類外皆愉,還需要修改工廠類(往工廠類中增加case語(yǔ)句呀),這種情況下艇抠,你的產(chǎn)品和你的工廠還是有較強(qiáng)的耦合的幕庐,要解決這個(gè)問(wèn)題,有兩個(gè)方法:
一是利用反射實(shí)現(xiàn)簡(jiǎn)單工廠模式家淤;二是使用工廠模式异剥。
在這之前,我們先來(lái)看看簡(jiǎn)單工廠模式中工廠類的另一種寫法:

    /// <summary>
    /// 簡(jiǎn)單工廠模式工廠類的另一種寫法
    /// </summary>
    public class MulWayTravelProductFactory
    {
        /// <summary>
        /// 直接指定創(chuàng)建的產(chǎn)品
        /// </summary>
        /// <returns>The domestic travel product.</returns>
        public static ITravelProduct createDomesticTravelProduct()
        {
            return new DomesticTravel();
        }

        public static ITravelProduct createOutboundTravelProduct()
        {
            return new OutboundTravel();
        }

        public static ITravelProduct createCruiseTravelProduct()
        {
            return new CruiseTravel();
        }
    }

此時(shí)絮重,查看國(guó)內(nèi)游線路列表的寫法如下:

Console.WriteLine("=========簡(jiǎn)單工廠模式工廠類的另一種寫法==========");
string domesticLineList = MulWayTravelProductFactory.createDomesticTravelProduct().GetLineList();
Console.WriteLine(domesticLineList);

輸出結(jié)果:

=========簡(jiǎn)單工廠模式工廠類的另一種寫法==========
您獲取到國(guó)內(nèi)游的線路列表啦冤寿!

這種方法雖然擺脫了switch,case語(yǔ)句青伤,但依然擺脫不了具體的產(chǎn)品類和工廠類的耦合督怜。所以,下面潮模,我們就來(lái)看看利用反射實(shí)現(xiàn)的簡(jiǎn)單工廠模式吧亮蛔!

利用反射實(shí)現(xiàn)的簡(jiǎn)單工廠模式

我們只需把工廠類生產(chǎn)產(chǎn)品的方式改成反射的方式即可痴施。這里主要是用到了反射中擎厢,根據(jù)類名獲取類實(shí)例的知識(shí)點(diǎn)究流,直接看代碼吧:

    /// <summary>
    /// 利用反射實(shí)現(xiàn)的簡(jiǎn)單工廠模式
    /// </summary>
    public class ReflectTravelProductFactory
    {
        /// <summary>
        /// Creates the travel product.
        /// </summary>
        /// <returns>The travel product.</returns>
        /// <param name="fullname">類名(包括命名空間的類名)</param>
        /// <typeparam name="T">The 1st type parameter.</typeparam>
        public static T createTravelProduct<T>(string fullname)
        {
            Assembly assenbly = Assembly.GetExecutingAssembly();
            return (T)assenbly.CreateInstance(fullname);
        }
    }

需要注意兩點(diǎn):

  • 參數(shù)fullname就是類名,但是完全限定名动遭,也就是說(shuō)要包括命名空間的
  • CreateInstance創(chuàng)建的類實(shí)例其實(shí)是object類型的芬探,需要強(qiáng)制轉(zhuǎn)換一下

此時(shí),查看國(guó)內(nèi)游線路列表的寫法為:

Console.WriteLine("=========利用反射實(shí)現(xiàn)的簡(jiǎn)單工廠模式==========");
string domesticLineList = ReflectTravelProductFactory.createTravelProduct<DomesticTravel>("factorypatterndemo.TravelProduct.DomesticTravel").GetLineList();
Console.WriteLine(domesticLineList);

輸出結(jié)果:

=========利用反射實(shí)現(xiàn)的簡(jiǎn)單工廠模式==========
您獲取到國(guó)內(nèi)游的線路列表啦厘惦!

此時(shí)偷仿,具體的產(chǎn)品類跟工廠類已經(jīng)完全解耦了。也就是說(shuō)宵蕉,如果需要做產(chǎn)品的擴(kuò)展酝静,只需要增加具體的產(chǎn)品類,無(wú)需修改工廠類了羡玛。
但這個(gè)方式唯一的缺點(diǎn)(準(zhǔn)確點(diǎn)講是問(wèn)題吧)就是反射的性能有待商榷别智。

工廠模式

上面所有方式的做法就是在一個(gè)工廠里生產(chǎn)了所有的產(chǎn)品,工廠模式的做法是抽象出一個(gè)工廠接口稼稿,然后不同的產(chǎn)品在不同的工廠中進(jìn)行生產(chǎn)薄榛。
(1)抽象出一個(gè)工廠接口:

    /// <summary>
    /// 抽象工廠接口
    /// </summary>
    public interface ITravelProductFactory
    {
        ITravelProduct getTravelProductType();//該接口有一個(gè)獲取旅游產(chǎn)品類型的行為
    }

(2)不同的產(chǎn)品建立不同的工廠
國(guó)內(nèi)游產(chǎn)品的工廠:

    /// <summary>
    /// 生產(chǎn)國(guó)內(nèi)游產(chǎn)品的工廠
    /// </summary>
    public class DomesticTravelFactory:ITravelProductFactory
    {
        public ITravelProduct getTravelProductType()
        {
            return new DomesticTravel();
        }
    }

出境游產(chǎn)品的工廠:

    /// <summary>
    /// 生產(chǎn)出境游產(chǎn)品的工廠
    /// </summary>
    public class OutboundTravelFactory:ITravelProductFactory
    {
        public ITravelProduct getTravelProductType()
        {
            return new OutboundTravel();
        }
    }

郵輪游產(chǎn)品的工廠:

    /// <summary>
    /// 生產(chǎn)郵輪游產(chǎn)品的工廠
    /// </summary>
    public class CruiseTravelFactory:ITravelProductFactory
    {
        public ITravelProduct getTravelProductType()
        {
            return new CruiseTravel();
        }
    }

此時(shí),查看國(guó)內(nèi)游線路列表的寫法如下:

Console.WriteLine("=========工廠模式==========");
ITravelProductFactory factory = new DomesticTravelFactory();
ITravelProduct domesticTravel= factory.getTravelProductType();
string domesticLineList = domesticTravel.GetLineList();
Console.WriteLine(domesticLineList);

輸出結(jié)果:

=========工廠模式==========
您獲取到國(guó)內(nèi)游的線路列表啦让歼!

如果要?擴(kuò)展?產(chǎn)品敞恋,只需要增加對(duì)應(yīng)的產(chǎn)品類和產(chǎn)品工廠即可。

大家知道谋右,OTA不止一家硬猫,每家都有國(guó)內(nèi)游、出境游倚评、郵輪游浦徊,此時(shí)抽象工廠模式即將登場(chǎng)。

抽象工廠

(1)分別建立國(guó)內(nèi)游天梧、出境游和郵輪游的抽象接口盔性,并繼承一開(kāi)始定義的ITravelProduct接口,這樣就可以共用ITravelProduct接口中定義的行為了呢岗。代碼如下:

    public interface IDomesticTravel:ITravelProduct
    {
    }
    public interface IOutboundTravel:ITravelProduct
    {
    }
    public interface ICruiseTravel:ITravelProduct
    {
    }

(2)定義一個(gè)OTA抽象工廠冕香,該工廠可以生產(chǎn)國(guó)內(nèi)游、出境游和郵輪游三種產(chǎn)品:

    public interface ITravelFactory
    {
        IDomesticTravel createDomesticTravel();
        IOutboundTravel createOutboundTravel();
        ICruiseTravel createCruiseTravel();
    }

(3)定義一家名叫XC的OTA工廠后豫,該工廠繼承ITravelFactory

    public class XC_Factory:ITravelFactory
    {
        public IDomesticTravel createDomesticTravel()
        {
            return new XC_DomesticTravel();
        }

        public IOutboundTravel createOutboundTravel()
        {
            return new XC_OutboundTravel();
        }

        public ICruiseTravel createCruiseTravel()
        {
            return new XC_CruiseTravel();
        }
    }

再定義一家名叫TC的OTA工廠:

    public class TC_Factory : ITravelFactory
    {
        public ICruiseTravel createCruiseTravel()
        {
            return new TC_CruiseTravel();
        }

        public IDomesticTravel createDomesticTravel()
        {
            return new TC_DomesticTravel();
        }

        public IOutboundTravel createOutboundTravel()
        {
            return new TC_OutboundTravel();
        }
    }

現(xiàn)在悉尾,我們想要獲取兩家OTA的所有國(guó)內(nèi)游線路列表:

Console.WriteLine("=========抽象工廠模式==========");
ITravelFactory tc_factory = new TC_Factory();
ITravelFactory xc_factory = new XC_Factory();
IDomesticTravel tc_domesticTravel = tc_factory.createDomesticTravel();
IDomesticTravel xc_domesticTravel = xc_factory.createDomesticTravel();
string tc_domesticLineList = tc_domesticTravel.GetLineList();
string xc_domesticLineList = xc_domesticTravel.GetLineList();
Console.WriteLine(tc_domesticLineList);
Console.WriteLine(xc_domesticLineList);

輸出結(jié)果:

=========抽象工廠模式==========
您獲取到TC的國(guó)內(nèi)游線路列表啦!
您獲取到XC的國(guó)內(nèi)游線路列表啦挫酿!

好啦构眯,到這里,工廠模式基本講結(jié)束了早龟”拱裕總結(jié)一下:

  • 工廠模式有三種:簡(jiǎn)單工廠猫缭、工廠和抽象工廠
  • 簡(jiǎn)單工廠模式中,產(chǎn)品類和工廠類之間存在耦合
  • 工廠模式只能解決單一商家多個(gè)產(chǎn)品的問(wèn)題
  • 抽象工廠模式可以解決多個(gè)商家多個(gè)產(chǎn)品的問(wèn)題

Q&A環(huán)節(jié)
Q:上面的代碼中都是使用接口來(lái)表示抽象產(chǎn)品或者抽象工廠壹店,可以改成用抽象類嗎猜丹?
(這個(gè)問(wèn)題翻譯一下,其實(shí)就是抽象類和接口的區(qū)別)
A:就本實(shí)例中的接口而言硅卢,完全可以使用抽象類替換射窒。但不是說(shuō)抽象類和接口就是等價(jià)的,不然為什么會(huì)同時(shí)存在呢将塑?存在即合理嘛脉顿,一般對(duì)象的抽象偏向使用抽象類,行為的抽象偏向使用接口点寥。抽象類中除了有無(wú)需實(shí)現(xiàn)的抽象方法弊予,也可以有具體實(shí)現(xiàn)的方法;而接口中只能有無(wú)實(shí)現(xiàn)的方法开财。一個(gè)類只能繼承一個(gè)父類汉柒,但可以繼承多個(gè)接口,所以我們?cè)谑褂媒涌诘臅r(shí)候有更好的擴(kuò)展性责鳍,這大概就是編程原則中有一條是針對(duì)接口編程碾褂,而不是針對(duì)類編程吧。

源碼地址:

差不多就這些了历葛。

說(shuō)些題外話正塌,最近看到身邊的同事小伙伴們?cè)谧约簣?jiān)持不懈地努力下,慢慢成長(zhǎng)和蛻變成更加優(yōu)秀的樣子恤溶,有時(shí)候我常常想乓诽,如果我一開(kāi)始認(rèn)識(shí)此刻的TA,大概都不敢和TA交朋友吧咒程。沒(méi)有誰(shuí)天生就是優(yōu)秀的鸠天,每個(gè)優(yōu)秀的人背后都付出了旁人難以想象和無(wú)法看到的努力和堅(jiān)持。也許我還是對(duì)自己要求太低了帐姻,由于種種原因放松了對(duì)自我成長(zhǎng)的要求稠集,看著自己與伙伴越來(lái)越大的差距,既羞愧又緊張饥瓷,也有不少壓力剥纷,也許是我還沒(méi)放棄自己吧,如果要自己立刻和如此優(yōu)秀的他們比呢铆,也許我連重新上路的勇氣都沒(méi)有晦鞋,所以就和昨天的自己比吧。其實(shí)這篇文章昨天就能完成的,但我還是拖延到今天才完成悠垛,不過(guò)好在今天完成了吼砂,說(shuō)明比昨天的我優(yōu)秀了一點(diǎn)點(diǎn),哈哈鼎文,只能這么安慰自己了,加油吧因俐!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拇惋,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子抹剩,更是在濱河造成了極大的恐慌撑帖,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件澳眷,死亡現(xiàn)場(chǎng)離奇詭異胡嘿,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)钳踊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門衷敌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人拓瞪,你說(shuō)我怎么就攤上這事缴罗。” “怎么了祭埂?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵面氓,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蛆橡,道長(zhǎng)舌界,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任泰演,我火速辦了婚禮呻拌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘睦焕。我一直安慰自己柏锄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布复亏。 她就那樣靜靜地躺著趾娃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缔御。 梳的紋絲不亂的頭發(fā)上抬闷,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼笤成。 笑死评架,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的炕泳。 我是一名探鬼主播纵诞,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼培遵!你這毒婦竟也來(lái)了浙芙?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤籽腕,失蹤者是張志新(化名)和其女友劉穎嗡呼,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體皇耗,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡南窗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了郎楼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片万伤。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖呜袁,靈堂內(nèi)的尸體忽然破棺而出壕翩,到底是詐尸還是另有隱情,我是刑警寧澤傅寡,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布放妈,位于F島的核電站,受9級(jí)特大地震影響荐操,放射性物質(zhì)發(fā)生泄漏芜抒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一托启、第九天 我趴在偏房一處隱蔽的房頂上張望宅倒。 院中可真熱鬧,春花似錦屯耸、人聲如沸拐迁。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)线召。三九已至,卻和暖如春多矮,著一層夾襖步出監(jiān)牢的瞬間缓淹,已是汗流浹背哈打。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留讯壶,地道東北人料仗。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像伏蚊,于是被迫代替她去往敵國(guó)和親立轧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • 1 場(chǎng)景問(wèn)題# 1.1 選擇組裝電腦的配件## 舉個(gè)生活中常見(jiàn)的例子——組裝電腦躏吊,我們?cè)诮M裝電腦的時(shí)候氛改,通常需要選...
    七寸知架構(gòu)閱讀 4,306評(píng)論 6 66
  • 設(shè)計(jì)模式匯總 一、基礎(chǔ)知識(shí) 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用颜阐、多...
    MinoyJet閱讀 3,922評(píng)論 1 15
  • 一、設(shè)計(jì)模式的分類 總體來(lái)說(shuō)設(shè)計(jì)模式分為三大類: 創(chuàng)建型模式吓肋,共五種:工廠方法模式凳怨、抽象工廠模式、單例模式是鬼、建造者...
    lichengjin閱讀 890評(píng)論 0 8
  • 一肤舞、設(shè)計(jì)模式的分類 總體來(lái)說(shuō)設(shè)計(jì)模式分為三大類: 創(chuàng)建型模式,共五種:工廠方法模式均蜜、抽象工廠模式李剖、單例模式、建造者...
    RamboLI閱讀 748評(píng)論 0 1
  • 我們小區(qū)有塊空地囤耳,每天晚上天未黑透婆婆大媽們就已經(jīng)三三兩兩過(guò)來(lái)篙顺。音響一開(kāi),舞動(dòng)起來(lái)充择。跳得好的德玫,前排領(lǐng)舞。新加入的椎麦,...
    遠(yuǎn)遠(yuǎn)的風(fēng)閱讀 152評(píng)論 0 0