昨天寫代碼的時(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),哈哈鼎文,只能這么安慰自己了,加油吧因俐!