前言:編程
除了使用new操做符以外,還有更多製造對象的方法。你將瞭解到實例化這個活動不該該老是公開的進行,也會意識到初始化會形成「耦合」的問題。工廠模式將會從複雜的依賴中幫你脫困。設計模式
當看到「new」,就會想到「具體」,的確也是在實例化一個具體的類,而不是接口。代碼綁着具體的類致使代碼更脆弱,更缺少彈性。當有一羣相關的具體類時,一般會有以下代碼:框架
Duck duck; If(picnic) duck=new MallardDuck(); else if(hunting) duck=new DecoyDuck(); else if(inBathTub) duck=new RubberDuck();
這樣的代碼一旦有變化或者擴展,就必須從新修改此段代碼進行檢查和修改,這樣的修改容易致使系統的維護和更新更難,也更容易犯錯。ide
針對接口編程,能夠隔離掉之後系統可能發生的一大堆改變,由於針對接口而寫,能夠經過多態,與任何新類實現該接口。當代碼使用具體類時,一旦加入新的一些具體類就必須改變代碼。這就違背了「對修改關閉」的原則。爲了解決這樣的問題,咱們能夠經過「找出變化,隔離並封裝變化「的方式來解決。this
現實場景:設計
披薩店生產披薩,當須要生產更多類型的披薩的時候,壓力來自於如何增長更多的披薩類型。code
public class Pizza { Pizza OrderPizza(stringpizzaType) { Pizza pizza; if (pizzaType.Equals("cheese")) pizza = newCheesePizza(); else if(pizzaType.Equals("greek")) pizza = newGreekPizza(); else if(pizzaType.Equals("pepperoni")) pizza = newPepperoniPizza(); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
如同開始是講的那樣,要新增新的pizza,就須要修改這段代碼,修改以下:對象
public class Pizza { Pizza OrderPizza(stringpizzaType) { Pizza pizza; if(pizzaType.Equals("cheese")) pizza = newCheesePizza(); else if(pizzaType.Equals("greek")) pizza = newGreekPizza(); else if(pizzaType.Equals("pepperoni")) pizza = newPepperoniPizza(); else if(pizzaType.Equals("clam")) //新增的pizza類型 pizza = newCalmPizza(); else if(pizzaType.Equals("veggie"))//新增的pizza類型 pizza = newVeggiePizza(); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
根據咱們上邊提到的「將變化抽離並封裝「的原則,咱們能夠將建立pizza實例這一塊給抽離出來,由於這塊後邊可能會新增一些別的pizza類型,由一個對象來專職建立pizza對象。咱們稱這個新對象爲」工廠「。代碼以下:繼承
public class SimplePizzaFactory { public PizzaCreatePizza(string pizzaType) { Pizza pizza = null; if(pizzaType.Equals("cheese")) pizza = newCheesePizza(); else if (pizzaType.Equals("pepperoni")) pizza = newPepperoniPizza(); else if(pizzaType.Equals("clam")) pizza = newCalmPizza(); else if(pizzaType.Equals("veggie")) pizza = newVeggiePizza(); return pizza; } }
這樣作的好處在於,把建立pizza對象的方法包裝成一個類,當之後實現改變時,只須要修改這個類便可。與此同時咱們還能夠把生成其餘類的方法也放在這個簡單的工廠中。接口
這樣咱們生成pizza類的代碼就變成以下的樣子:
public class Pizza { Pizza OrderPizza(stringpizzaType) { Pizza pizza; SimplePizzaFactorysimplePizzaFactory = new SimplePizzaFactory();//生成pizza pizza =simplePizzaFactory.CreatePizza(pizzaType); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
通過這樣一系列的修改,咱們的Pizza的類圖就變成以下的樣子:
雖然咱們一直在說簡單的工廠,但事實上簡單工廠並非一個設計模式,更像是一種編程習慣。這裏講簡單的工廠,主要是爲了引出下面的兩個重量級的模式,它們都是工廠。
在1中,咱們經過簡單的工廠解決了生產不一樣披薩的問題,可是,若是有新的加盟店加盟進來,如何解決不一樣加盟店的區域差別、質量問題呢?或許,咱們能夠像1中那樣利用簡單的工廠,對應不一樣地區的加盟店建立不一樣的工廠。
這樣作致使的另外一個問題就是,不一樣的加盟店披薩的製做流程、方法可能不一樣,如何才能把加盟店和建立披薩捆綁在一塊兒的同時又保持必定的彈性?
咱們能夠把CreatePizza()方法放回到PizzaStore中,可是要把它設置爲抽象方法,而後爲每個區域加盟店建立一個PizzaStore的子類。
以下所示:
public abstract class PizzaStore { public PizzaOrderPizza(string pizzaType) { Pizza pizza =CreatePizza(pizzaType); pizza.Prepare(); pizza.Bake(); pizza.Cut(); pizza.Box(); return pizza; } public abstract PizzaCreatePizza(string pizzaType);//把工廠對象移到該方法中,該方法爲抽象方法 }
如今咱們有了PizzaStore超類,讓各個不一樣地域的加盟店繼承此超類便可。具體的類圖以下:
本來是由一個對象複製全部具體類的實例化,如今經過對PizzaStore作一些轉變,變成由一羣子類負責實例化。
工廠方法用來處理對象的建立,並將這樣的行爲封裝在子類中。這樣客戶程序中關於超類的代碼就和子類對象建立代碼解耦。
public abstract class PizzaStore { public Pizza OrderPizza(stringtype) { Pizza pizza = CreatePizza(type); pizza.Prepare(); pizza.Bake(); pizza.Cut(); pizza.Box(); return pizza; } public abstract Pizza CreatePizza(stringtype);//抽象出建立Pizza的工廠方法,由子類實現該方法並建立具體的Pizza }
public class MYPizzaStore:PizzaStore { public override Pizza CreatePizza(string type) { Pizza pizza=null; switch(type) { case "cheese": pizza = new NYStyleCheesePizza(); break; case "veggie": pizza=new NYStyleVeggiePizza(); break; case "clam": pizza=new NYStyleClamPizza(); break; case "pepperoni": pizza=new NYStylePepperoniPizza(); break; } return pizza; } }
public abstract class Pizza { public string name; public string dough; public string sauce; public ArrayList toppings = newArrayList(); public void Prepare() { System.Console.WriteLine("Preparing" + name); System.Console.WriteLine("Tossingdough..."); System.Console.WriteLine("Addingsauce.."); System.Console.WriteLine("Addingtoppings: "); for(int i = 0; i < toppings.Count; i++) { System.Console.WriteLine(" "+ toppings[i]); } } public void Bake() { System.Console.WriteLine("Bakefor 25 minutes at 350"); } public void Cut() { System.Console.WriteLine("Cuttingthe pizza into diagonal slices"); } public void Box() { System.Console.WriteLine("Placepizza in official PizzaStore box"); } public string GetName() { return name; } }
public class NYStyleCheesePizza : Pizza { public NYStyleCheesePizza() { name = "NY StyleSauc and Cheese Pizza"; dough="Thin Crust Dough"; sauce="Marinara Sauce"; toppings.Add("GratedReggiano Cheese"); } }
全部工廠模式都用來封裝對象建立,工廠方法模式經過讓子類決定該建立的對象是什麼,來達到將對象建立的過程封裝的目的。
工廠方法模式定義了一個建立對象的接口,但由子類決定要實例化的類是哪個,工廠方法讓類把實例化推遲到子類。
建立者(Creator)類
產品類
簡單工廠和工廠方法之間的差別?
簡單工廠是在一個地方把全部的事都處理完了,然而工廠方法倒是建立一個框架,讓子類決定要如何實現。簡單工廠的作法,能夠將對象的建立封裝起來,可是簡單工廠不具有工廠方法的彈性,由於簡單工廠不能變動正在建立的產品。
要依賴抽象,不要依賴具體類
不能讓高層組件依賴底層組件,並且無論高層、底層組件,二者都應該依賴於抽象。
如何避免違反依賴倒置原則:
l 變量不能夠持有具體類的引用。
若是使用new,則會持有具體類的引用,能夠用工程來避開這樣的作法
l 不要讓類派生自具體類。
若是派生自具體類,你就會依賴具體類(請派生自一個抽象類或接口)
l 不要覆蓋基類中已實現的方法。
若是覆蓋基類已實現的方法,那麼你的基類就不是一個真正適合被繼承的抽象。基類中已實現的方法,應該由全部的子類共享。
回到上文的Pizza店,如今有新的需求,想要確保每家加盟店使用高質量的材料,打算建立一家生產原料的加工廠,並將原料送到各個加盟店。這個工廠負責建立原料家族中的每一種原料,工廠須要生產麪糰、醬料、芝士等。先爲工廠定義一個接口,該接口負責全部原料:
public interface PizzaIngredientFactory { Dough CreateDough(); Sauce CreateSauce(); Cheese CreateCheese(); Veggies[] CreateVeggies(); Pepperoni CreatePepperoni(); Clams CreateClam(); }
public class NYPizzaIngredientFactory : PizzaIngredientFactory//具體原料工廠必須實現這個接口 { public Dough CreateDough() { return new ThinCrustDough(); } public Sauce CreateSauce() { return new MarinaraSauce(); } public Cheese CreateCheese() { return new ReggianoCheese(); } public Veggies[] CreateVeggies() { Veggies[] veggies = new Veggies[]{ new Garlic(), new Onion(), new Mushroom(), new RedPepper() }; return veggies; } public Pepperoni CreatePepperoni() { return new SlicedPepperoni(); } public Clams CreateClam() { return new FreshClams(); } }
public abstract class Pizza { public string name; public Dough dough; public Sauce sauce; public Veggies[] veggies; public Cheese cheese; public Pepperoni pepperoni; public Clams clam; public ArrayList toppings = newArrayList(); public abstract void Prepare();//把Prepare()方法聲明成抽象,在這個方法中,咱們須要收集Pizza所需的原材料,而這些原材料來自原料工廠。 public void Bake() { System.Console.WriteLine("Bakefor 25 minutes at 350"); } public void Cut() { System.Console.WriteLine("Cuttingthe pizza into diagonal slices"); } public void Box() { System.Console.WriteLine("Placepizza in official PizzaStore box"); } public string GetName() { return name; } }
public class NYStyleCheesePizza : Pizza { PizzaIngredientFactory ingredientFactory; public NYStyleCheesePizza(PizzaIngredientFactoryingredientFactory)//製做Pizza須要工廠提供原材料,
因此每一個pizza類都須要從構造器中獲得一個工廠,並將工廠存儲在變量中 { this.ingredientFactory =ingredientFactory; name = "NY StyleSauc and Cheese Pizza"; toppings.Add("GratedReggiano Cheese"); } public override void Prepare() { System.Console.WriteLine("Preparing" + name); dough = ingredientFactory.CreateDough(); sauce = ingredientFactory.CreateSauce(); cheese = ingredientFactory.CreateCheese(); } }
public class MYPizzaStore:PizzaStore { public override Pizza CreatePizza(string type) { Pizza pizza=null; PizzaIngredientFactory ingredientFactory= new NYPizzaIngredientFactory(); switch(type) { case "cheese": pizza = new NYStyleCheesePizza(ingredientFactory); break; case "veggie": pizza=new NYStyleVeggiePizza(ingredientFactory); break; case "clam": pizza=new NYStyleClamPizza(ingredientFactory); break; case "pepperoni": pizza=new NYStylePepperoniPizza(ingredientFactory); break; } return pizza; } }
經過這一系列的操做,咱們引入了新類型的工廠,也就是所謂的「抽象工廠」,來建立pizza原來家族。經過抽象工廠所提供的接口建立產品家族,利用這個接口書寫代碼,咱們的代碼將從實際工廠解耦,以便在不一樣上下文中實現各式各樣的工廠,製造出各類不一樣的產品。
抽象工廠模式提供一個接口,用於建立相關或依賴對象家族,並且不須要致命具體類。
抽象工廠模式容許客戶使用抽象的接口來建立一組相關的產品,而不須要知道實際產出的具體產品是什麼,客戶就從具體的產品中被解耦。
抽象工廠和工廠方法都是負責建立對象。
抽象工廠是經過對象的組合
定義了一個建立對象的接口,但因爲子類決定要實例化的類是哪個,工廠方法讓類把實例化推遲到子類。
因此利用工廠方法建立對象時,須要擴展一個類,並覆蓋它的工廠方法。
整個工廠方法模式,只不過就是經過子類來建立對象,這種作法,客戶只須要知道他們所使用的抽象類型就能夠了,而由子類負責決定具體類型。將客戶從具體類型中解耦。
工廠方法是繼承。
抽象工廠方法是將一羣相關的產品集合起來,用於建立相關或依賴對象的家族,而不須要明確指定具體類。