本文出自 http://blog.csdn.net/shuangde800
——所有工廠都是用來封裝對象的建立
——工廠是很有威力的技巧,協助我們針對抽象編程,而不要針對具體類編程。
認識抽象原廠模式
在之前的原廠模式已經建立了一個比薩工廠,但是真正要生產的產品——比薩,卻還沒有涉及到。
比薩店會生產很多不同的比薩產品,而比薩又有格式不同風格的原料。
所以,我們要建造一個工廠來生產原料,這個工廠將負責建立原料家族中的每一個原料:醬料、芝士、麵糰等。
開始先為工廠定義一個介面,這個介面負責建立所有的原料:
public interface PizzaIngredientFactory {// 介面中,每個原料都有一個對應的方法建立該原料// 下面方法返回各原料對象public Dough createDough();public Sauce createSauce();public Cheese createCheese();public Veggies[] createVeggies();public Pepperoni createPepperoni();public Clams createClam(); }
下面要做的是:
1、給每個比薩店建造一個工廠。需要建立實現PizzaIngredientFactory介面的類,實現介面的方法。
2、實現一組原料類供工廠使用。
3、把這一切組織起來,將新的原料工廠整合進舊的PizzaStore中。
下面建立了紐約原料工廠,實現了PizzaIngredientFactory介面:
public class NYPizzaIngredientFactory implements 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 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 {String name;Dough dough;Sauce sauce;Veggies veggies[];Cheese cheese;Pepperoni pepperoni;Clams clam;abstract void prepare();//把prepare申明為抽象,在這個方法中,需要手機比薩所需的原料,而這些原料當然是來自原料工廠了void bake() {System.out.println("Bake for 25 minutes at 350");}void cut() {System.out.println("Cutting the pizza into diagonal slices");}void box() {System.out.println("Place pizza in official PizzaStore box");}void setName(String name) {this.name = name;}String getName() {return name;}public String toString() {StringBuffer result = new StringBuffer();result.append("---- " + name + " ----\n");if (dough != null) {result.append(dough);result.append("\n");}if (sauce != null) {result.append(sauce);result.append("\n");}if (cheese != null) {result.append(cheese);result.append("\n");}if (veggies != null) {for (int i = 0; i < veggies.length; i++) {result.append(veggies[i]);if (i < veggies.length-1) {result.append(", ");}}result.append("\n");}if (clam != null) {result.append(clam);result.append("\n");}if (pepperoni != null) {result.append(pepperoni);result.append("\n");}return result.toString();}}
然後就是建立真正的比薩了! 比薩店生產的比薩都是繼承自Pizza類,假設要生產一個乳酪比薩(CheesePizza):
// 這是應用了簡單原廠模式// 比薩要用到原料,把生產原料任務委託給了ingredientFactory工廠public class CheesePizza extends Pizza {PizzaIngredientFactory ingredientFactory; public CheesePizza(PizzaIngredientFactory ingredientFactory) {this.ingredientFactory = ingredientFactory;} // 實現prepare方法void prepare() {System.out.println("Preparing " + name);dough = ingredientFactory.createDough();sauce = ingredientFactory.createSauce();cheese = ingredientFactory.createCheese();}}
上面這個真正的比薩,是不是很眼熟?沒錯,起始就是簡單原廠模式, Pizza利用相關的工廠生產原料。所生產的原料依賴所使用的工廠,Pizza類根本不關心這些原料,它只知道如何製作比薩。現在,Pizza和原料之間被解耦,無論原料工廠是在北京還是上海,Pizza類都可以輕易地複用。
最後,是時候讓新的比薩店登場了!
public class NYPizzaStore extends PizzaStore { protected Pizza createPizza(String item) {Pizza pizza = null;// 原料工廠負責生產所有的原料PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory(); if (item.equals("cheese")) {pizza = new CheesePizza(ingredientFactory); // 把工廠傳遞給每一個比薩,以便使比薩能從工廠中取得原料pizza.setName("New York Style Cheese Pizza"); } else if (item.equals("veggie")) { pizza = new VeggiePizza(ingredientFactory);pizza.setName("New York Style Veggie Pizza"); } else if (item.equals("clam")) { pizza = new ClamPizza(ingredientFactory);pizza.setName("New York Style Clam Pizza"); } else if (item.equals("pepperoni")) {pizza = new PepperoniPizza(ingredientFactory);pizza.setName("New York Style Pepperoni Pizza"); } return pizza;}
小結:
上面一連串代碼到底做了什嗎?
其實就做了一件事,引入了新類型的工廠——抽象工廠,來建立比薩原料家族。
我們建立了介面類PizzaIngredientFactory,這個介面可以建立產品的家族,利用這個介面,我們的代碼將從實際工廠解耦,以便在不同上下文中實現各式各樣的工廠,製作出不同的產品。
定義抽象原廠模式
抽象工廠提供一個介面,用於建立相關或依賴對象的家族,而不需要明確指定具體類。
抽象工廠允許客戶使用抽象的介面來建立一組相關的產品,而不需要知道(或關心)實際產出的具體產品是什麼。這樣一來,客戶就從具體的產品中解藕。
生產原料的類圖:
我們可以發現抽象工廠主要是為“產品”而服務的。
抽象工廠的方法經常以Factory 方法的方式實現(例如createDough(), createSource(),每個方法都被聲明為抽象,而子類的方法覆蓋這些方法來建立某些對象),抽象工廠的任務是定義一個負責建立一組產品的介面。這個介面內的每個方法都負責建立一個具體產品,同時我們利用實現抽象工廠的子類來提供這些具體的做法,所以,在抽象工廠中利用Factory 方法實現生產的做法是相當自然的做法。
比較Factory 方法和抽象工廠
抽象工廠和Factory 方法都是負責建立對象,但Factory 方法用的是繼承,而抽象方法用的是對象的組合
利用Factory 方法建立對象,需要拓展一個類,並覆蓋它的Factory 方法。整個Factory 方法模式,只不過就是通過子類來建立對象。用這種做法,客戶只需要知道他們所使用的抽象類別型就可以了,而由子類負責決定具體的類型,將客戶從具體類型中解耦。
而抽象工廠用來建立一個產品家族的抽象類別型,這個類型的子類定義了產品被產生的方法。要想使用這個工廠,必須先執行個體化它,然後將它傳入一些針對抽象類別型所寫的代碼中。所以和Factory 方法一樣,也實現解耦。
總之,他們都能將對象的建立封裝起來,使應用程式解耦,並降低對特定實現的依賴。