三兄弟模式 —簡單工廠&工廠方法&抽象工廠
最近學習設計模式時,遇到了“三兄弟模式”,這三個傢伙可把我搞暈了,通過查閱資料和個人理解,整理出了這篇文章,希望能幫到大家。
簡單工廠:
簡單工廠模式中,包括一個“抽象產品類”(該類可以是介面Interface,也可以是實際的類Class),所有需要的產品類都是該“抽象產品類”的子類(如果是介面的話,那麼就是說所有產品類都繼承了該介面)。
另外還包含一個具體的工廠類,由該工廠類生成所有的產品類的物件,其內部一般是類似於switch的結構,根據選擇來建立不同型別的物件。
用小米公司生產手機做個例子:這裡的“小米手機”就是“抽象產品類”,而具體的產品如“小米3、紅米”等是繼承了“小米手機”的子類,
程式碼結構圖:
程式碼:
//製造小米手機類
public class MiPhone
{
public virtual void Show()
{
Console.WriteLine(" ");
}
}
//具體手機類 class Mi3 : MiPhone { public override void Show() { Console .WriteLine ("我是速度最快的小米3! "); } } class HoneMi : MiPhone { public override void Show() { Console.WriteLine("我是價效比最高的紅米! "); } } class Mi2S : MiPhone { public override void Show() { Console.WriteLine("額...我是一直降價的小米2S - -! "); } }
// 簡單小米工廠類 public class XiaoMiFactory { public static MiPhone CreateXiaoMiPhone(string Model) { MiPhone pho = null; switch (Model) { case "Mi3": pho = new Mi3(); break; case "HongMi": pho = new HoneMi(); break; case "Mi2S": pho = new Mi2S(); break; } return pho; } }
//客戶端程式碼
<pre class="csharp" code_snippet_id="246967" snippet_file_name="blog_20140320_5_2344108" name="code"> static void Main(string[] args)
{
MiPhone pho;
pho = XiaoMiFactory.CreateXiaoMiPhone("Mi3");
pho.Show();
Console.WriteLine();
pho = XiaoMiFactory.CreateXiaoMiPhone("Mi2S");
pho.Show();
Console.WriteLine();
pho = XiaoMiFactory.CreateXiaoMiPhone("HongMi");
pho.Show();
Console.WriteLine();
}
顯示: 優點:工廠類中包含了必要的邏輯判斷,根據客戶端的條件選擇來動態的例項化相關的類,對於客戶端來說去除了與具體產品的依賴。
缺點:很明顯就是小米公司每發明一個新型號的手機都需要修改簡單工廠類(增加case判斷),違反了封閉修改,開放擴充套件原則。這可怎麼解決呢?不用擔心,在下面會找到答案的。
工廠方法:
它的核心是:定義一個用於建立物件的介面,讓子類決定例項化哪一個類。工廠方法使一個類的例項化延遲到其子類。具體來說,該模式把工廠類抽象出了一個介面(該介面只有建立抽象產品的工廠方法),其他具體生成物件的工廠去實現這個介面。注意工廠方法的每個具體工廠只負責返回一種產品類。
沒有例子是很難理解的,同樣以小米手機生產做例子:
科技在發展,時代在進步。小米公司即將推出一款價效比超越紅米的霸氣神雞,名之曰“紅米Note”(3月26日開放購買,想換手機的同學趕緊預約哦!)但是隨著問題就來了,如果小米公司用簡單工廠模式的話,那麼增加一款紅米Note,就需要新增一個紅米Note類,還需要修改小米工廠,在Switch中增加分支,這就違反了開閉原則。那麼就試試新學的工廠方法模式。
首先定義工廠抽象介面interfaceXiaoMiFactory,然後再新增具體生產手機的工廠,米3工廠,紅米工廠等
程式碼結構圖:
程式碼:
//小米手機類
public class MiPhone
{
public virtual void Show()
{
Console.WriteLine(" ");
}
}
//具體手機類
class Mi3 : MiPhone
{
public override void Show()
{
Console.WriteLine("我是速度最快的小米3! ");
}
}
class Mi2S : MiPhone
{
public override void Show()
{
Console.WriteLine("額...我是一直降價的小米2S - -! ");
}
}
class HoneMiNote : MiPhone
{
public override void Show()
{
Console.WriteLine("我是紅米Note,價效比歷史新高! ");
}
}
// 小米工廠介面
interface XiaoMiFactory
{
MiPhone CreatXiaoMiPhone();
}
// 製造小米3 的工廠
class Mi3Factory : XiaoMiFactory
{
public MiPhone CreatXiaoMiPhone()
{
return new Mi3();
}
}
// 製造 紅米Note 的工廠
class HongMiNoteFactory : XiaoMiFactory
{
public MiPhone CreatXiaoMiPhone()
{
return new HoneMiNote();
}
}
// 製造小米2S 的工廠
class Mi2SFactory : XiaoMiFactory
{
public MiPhone CreatXiaoMiPhone()
{
return new Mi2S();
}
}
//客戶端程式碼
static void Main(string[] args)
{
XiaoMiFactory factory = new HongMiNoteFactory();
MiPhone hongminote = factory.CreatXiaoMiPhone();
hongminote.Show();
Console.WriteLine();
}
顯示效果:
優點:這樣做使得,新出一個小米手機,只需新增一個該型號的具體工廠而無需修改原來的程式碼。符合開閉原則降低了客戶程式與產品物件的耦合。
缺點:任何事都有兩面性,每增加一種新產品,都需要加一個該產品工廠類,這就增加了額外開發量。
事實上,小米公司在生產手機的時候也會根據每個手機的型號生產出對應的充電器等配件。在只有一種產品的時候,是隻需要工廠方法模式的,而這種涉及到多個產品系列的問題時,就需要另一種專門的工廠模式——抽象工廠模式了。
抽象工廠:核心-提供一個建立一系列相關或相互依賴物件的介面,而無需指定他們具體的類。該模式和工廠方法模式很相似,也是一個抽象工廠介面和若干個具體的工廠,而不同的是抽象工廠介面定義了多個虛工廠方法,每個虛工廠方法負責製造一種產品,多個工廠方法返回多種產品,並且這些產品具有某些聯絡。
我們接著來舉小米手機例子:上面已經說到了小米公司在生產手機的時候也會根據每個手機的型號生產出對應的充電器等配件,這就相當於兩種相互聯絡的產品。XiaoMiFactory會定義兩個虛工廠方法,一個是CreateXiaoMiPhone用來生產手機,另一個是CreateXiaoMiCharger用於生產對應型號的手機充電器(在這裡假定不同型號手機使用不同的充電器)。
程式碼結構圖:
程式碼例項:
//小米手機類
public class MiPhone
{
public virtual void Show()
{
Console.WriteLine(" ");
}
}
//小米充電器類
public class MiCharger
{
public virtual void Show1()
{
Console.WriteLine(" ");
}
}
//具體手機類
class Mi3 : MiPhone
{
public override void Show()
{
Console.WriteLine("我是速度最快的小米3! ");
}
}
class HoneMiNote : MiPhone
{
public override void Show()
{
Console.WriteLine("我是紅米Note,價效比歷史新高! ");
}
}
//具體充電器類
class Mi3Charger : MiCharger
{
public override void Show1()
{
Console.WriteLine("我是小米3的專屬充電器 ! ");
}
}
class HoneMiNoteCharger : MiCharger
{
public override void Show1()
{
Console.WriteLine("我是紅米Note的專屬充電器! ");
}
}
// 小米工廠介面
interface XiaoMiFactory
{
MiPhone CreatXiaoMiPhone();
MiCharger CreatXiaoMiCharger();
}
// 製造 紅米Note 的工廠
class HongMiNoteFactory : XiaoMiFactory
{
public MiPhone CreatXiaoMiPhone()
{
return new HoneMiNote();
}
public MiCharger CreatXiaoMiCharger()
{
return new HoneMiNoteCharger();
}
}
// 製造小米3 的工廠
class Mi3Factory : XiaoMiFactory
{
public MiPhone CreatXiaoMiPhone()
{
return new Mi3();
}
public MiCharger CreatXiaoMiCharger()
{
return new Mi3Charger();
}
}
客戶端程式碼: static void Main(string[] args)
{
XiaoMiFactory factory = new XiaoMiFactory();
MiPhone hongminote = factory.CreatXiaoMiPhone();
MiCharger hongminotecharger = factory.CreatXiaoMiCharger();
hongminote.Show();
hongminotecharger.Show1();
Console.WriteLine();
}
顯示效果:
優點:使具體建立例項的過程與客戶端分離,隔離了具體類的生產,使得客戶並不需要知道什麼被建立。當增加新手機和充電器的時候,只需要增加相應的具體工廠和具體類即可,符合開閉原則和依賴倒轉原則。
缺點:增加新的產品等級結構很複雜,需要修改抽象工廠和所有的具體工廠類,對“開閉原則”的支援呈現傾斜性。如果要增加移動電源這項產品就需要增加電源基類,紅米Note電源類,Mi3電源類等,還要改動工廠介面和具體工廠,這顯然又不符合開閉原則。
小結:
1、簡單工廠對於產品的建立,由一個工廠類來控制,如果增加新產品,那麼增加新產品類的同時,還要對工廠類做相應的修改,增加case分支,這和開閉原則的違背的。(這個模式適用於一樣不會增加新產品的情況)
2、工廠方法對於產品的建立則是由各具體產品工廠負責(每個產品都會建有工廠),獨立進行建立的,這樣在新增產品時,只需增加相應工廠和產品類即可,保證的開閉原則。
3、抽象工廠,我們可以把工廠方法理解為特殊的抽象工廠,這個產品結構屬於一種型別有多個產品。而如果是幾種型別的,每個型別又有多個產品,這時就需要用到抽象工廠來實現了。
天下沒有完美的設計模式,每個模式都有其優缺點,我們需要做的就是在合適的場合利用合適的設計模式,這樣才能發揮出模式的最大能量,設計出優美的程式。