設計模式:第四篇--工廠模式
一、簡單工廠
簡單工廠模式:書中提到簡單工程其實並沒有獨立為一個“模式”,只是一個程式設計習慣。用的人比較多了,就總結出來了。
理解:其實這和我們現實當中的建立工廠生成是一樣的。比如手機生產車間,需要組裝一款手機,但是由很多型號,不同的型號配置不同的配件,不同的配件可以歸類,歸類其實就是歸類到簡單工廠裡面。同類配件不同型號由一種工廠生產,比如晶片;而另一類配件由另一個工廠生產。這些配件被生產出來後送到組裝車間。組裝的流程是不變的,都是再組裝同一個牌子的手機。僅僅是將變化的物件抽取到一個“工廠”去生產。

簡單工廠
案例:PizzaStore
需求:現在有一家pizza店,由於門店擴張,每個店面招師傅做pizza的話無法保證口味,於是店主想把師傅集中起來,簡單建一個pizza專門做pizza,送到門店銷售。
思考:抽象一個Pizza類,定義好做pizza的方法
/** * Project <demo-project> * Created by jorgezhong on 2018/9/20 8:56. * <p> * Pizza定義了Pizza的製作工序 */ public abstract class Pizza { public void prepare() { System.out.println("準備材料"); } public void bake() { System.out.println("烤"); } public void cut() { System.out.println("切"); } public void box() { System.out.println("包裝"); } }
public class CheesePizza extends Pizza { @Override public void prepare() { super.prepare(); System.out.println("prepare cheese"); } }
public class ClamPizza extends Pizza { @Override public void prepare() { super.prepare(); System.out.println("prepare clam"); } }
public class PepperoniPizza extends Pizza { @Override public void prepare() { super.prepare(); System.out.println("prepare pepperoni"); } }
public class VeggiePizza extends Pizza { @Override public void prepare() { super.prepare(); System.out.println("prepare vegetable"); } }
/** * Project <demo-project> * Created by jorgezhong on 2018/9/20 9:03. * 定義工廠 */ public class SimplePizzaFactory { public Pizza createPizza(String type) { Pizza pizza = null; if ("cheese".equals(type)) { pizza = new CheesePizza(); } else if ("pepperoni".equals(type)) { pizza = new PepperoniPizza(); } else if ("clam".equals(type)) { pizza = new ClamPizza(); } else if ("veggie".equals(type)) { pizza = new VeggiePizza(); } return pizza; } }
/** * Project <demo-project> * Created by jorgezhong on 2018/9/20 9:01. */ public class PizzaStore { SimplePizzaFactory factory; public PizzaStore(SimplePizzaFactory factory) { this.factory = factory; } public Pizza orderPizza(String type) { //工廠生產需要的pizza Pizza pizza = factory.createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
總結:簡單工程僅僅是抽象例項化物件的過程的程式碼而已。參考了人類工業化過程的做法。但是這個工廠可以有很多,方便拓展。由於獨立於門店,披薩的生產可以拓展,多元化生產不同的產品,門店也可以多元化經營。後續門店拓展和產品拓展的時候就很方便了。比如西面的需求
需求:現在pizza擴張門店了,在芝加哥和紐約開發開分店。但是過了一段時間發現賣不出去,經過市場調研,改良了符合當地口味的pizza。
思考:這不正好嗎,我開兩家工廠生產符合當地口味的不就完了,門店所有的其他訂購流程都不需要變。只需要提供不同工廠生產的pizza就可以。
/** * Project <demo-project> * Created by jorgezhong on 2018/9/20 9:03. * 芝加哥工廠 */ public class ChicagoPizzaFactory extends SimplePizzaFactory{ @Override public Pizza createPizza(String type) { Pizza pizza = null; if ("cheese".equals(type)) { pizza = new ChicagoStyleCheesePizza(); } else if ("pepperoni".equals(type)) { pizza = new ChicagoPepperoniPizza(); } else if ("clam".equals(type)) { pizza = new ChicagoClamPizza(); } else if ("veggie".equals(type)) { pizza = new ChicagoVeggiePizza(); } return pizza; } }
/** * Project <demo-project> * Created by jorgezhong on 2018/9/20 9:03. * 紐約工廠 */ public class NYPizzaFactory extends SimplePizzaFactory{ @Override public Pizza createPizza(String type) { Pizza pizza = null; if ("cheese".equals(type)) { pizza = new NYStyleCheesePizza(); } else if ("pepperoni".equals(type)) { pizza = new NYPepperoniPizza(); } else if ("clam".equals(type)) { pizza = new NYClamPizza(); } else if ("veggie".equals(type)) { pizza = new NYVeggiePizza(); } return pizza; } }
/** * Project <demo-project> * Created by jorgezhong on 2018/9/20 10:23. */ public class ChicagoStylePizzaStore { SimplePizzaFactory factory; public ChicagoStylePizzaStore(SimplePizzaFactory factory) { this.factory = factory; } public Pizza orderPizza(String type) { //工廠生產需要的pizza Pizza pizza = factory.createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
/** * Project <demo-project> * Created by jorgezhong on 2018/9/20 10:23. */ public class NYStylePizzaStore { SimplePizzaFactory factory; public NYStylePizzaStore(SimplePizzaFactory factory) { this.factory = factory; } public Pizza orderPizza(String type) { //工廠生產需要的pizza Pizza pizza = factory.createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }

pizza

簡單工廠
使用工廠製造不同風味的pizza
public class PizzaStoreDrive { public static void main(String[] args) { NYStylePizzaStore nyStylePizzaStore = new NYStylePizzaStore(new NYPizzaFactory()); nyStylePizzaStore.orderPizza("cheese"); nyStylePizzaStore.orderPizza("clam"); ChicagoStylePizzaStore chicagoStylePizzaStore = new ChicagoStylePizzaStore(new ChicagoPizzaFactory()); chicagoStylePizzaStore.orderPizza("cheese"); chicagoStylePizzaStore.orderPizza("clam"); } }
總結:總感覺哪裡不對是吧。感覺規劃好了工廠都是簡單工廠的模式,生產的pizza滿足需求了,但是門店是不是太靈活了店。如果能好好管理門店就好了。
二、工廠方法
工廠方法模式:通過讓子類決定該建立的物件是什麼,來達到將物件建立的過程封裝的目的。該模式定義了一個建立物件的介面,但是由子類決定例項化的物件。工廠方法讓類把例項化的動作推辭到子類。其實就是抽象的運用了。
需求:現在pizza店需要的很厲害了,不好管理,出現了各地師傅烘焙方法和流程不一樣的情況,味道變化很大。需要各地門店按照總店的烘焙方法和包裝方式來。
思考:我們發現決定當地風味的只有當地的門店和送到門店的工廠,也就是要由下面的門店來決定要哪個工廠生產的pizza。同時每個門店需要按照總店的加工流程來加工送過來的pizza。這樣我們把變化的部分抽象出來就好了,不變的由子類繼承即可。
1、將製造pizza這個動作從訂購pizza抽象出來,甚至訂購pizza的方法可以配置成final
/** * Project <demo-project> * Created by jorgezhong on 2018/9/20 9:01. */ public abstract class PizzaStore { SimplePizzaFactory factory; public PizzaStore(SimplePizzaFactory factory) { this.factory = factory; } public Pizza orderPizza(String type) { //工廠生產需要的pizza Pizza pizza = createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } /** * 抽象工廠方法 * * @param type * @return */ abstract Pizza createPizza(String type); }
public class ChicagoStylePizzaStore extends PizzaStore { public ChicagoStylePizzaStore(SimplePizzaFactory factory) { super(factory); } @Override Pizza createPizza(String type) { return factory.createPizza(type); } }
public class NYStylePizzaStore extends PizzaStore { public NYStylePizzaStore(SimplePizzaFactory factory) { super(factory); } @Override Pizza createPizza(String type) { return factory.createPizza(type); } }

抽象工廠方法
關鍵在於抽象工廠方法:我們看下圖關鍵程式碼對比一下,將factory.createPizza()抽象出來,可以由子類去決定由返回的pizza。本來由門店的訂購流程決定的,由工廠生產pizza程式設計由下面門店決定用哪個工廠生產的pizza,而由總店決定訂購流程。注意:這裡抽象的是工廠的方法,就是原來工廠呼叫方法抽象出來了一層。下沉到子類。

前後對比
總結:到這裡我們其實已經看到了無論式pizza店還是pizza他們是創造和被創造的關係。由於拓展的需要,引入了第三方factory協調。分離了pizza店的創造職能。仔細觀察我們的拓展方式,我們要拓展很多門店,同時要拓展很多pizza。因此這兩者也是平級的拓展關係。簡單工廠模式是簡單的分離創造者(PizzaStore)的職能,重產品(Pizza)的生產,忽略了創造者的管理。而工廠方法模式是補充了簡單工廠的缺點,在創造者身上開一個介面,抽象一層介面(createPizza方法)出來,讓子類去實現。達到了管理創造者的效果。
核心思想:抽象的靈活運用,從簡單工廠和工廠方法,體會到了抽象程式碼運用的妙處,可將程式碼抽象出一個角色去執行這段程式碼達到“協調”的效果,也可以抽象一段程式碼到方法,下沉程式碼,達到“管理”的效果。這裡其實我們引入了一個設計原則
依賴倒置原則:依賴抽象,而不依賴具體
所謂倒置也是從具體到抽象的抽象方式,從底層開始往上抽象,而不是從頂層開始往下抽象。
三、抽象工廠
抽象工廠模式:小編理解的其實就是提供一個介面,定義物件的家族,抽象管理起來。並運用工廠方法來決定具體物件的建立,與工廠方法不同的是,抽象工廠更偏向於“組合”,定義物件家族範圍更大。
需求:現在pizza店多了,在根據門店需求製作不同地域風味的pizza的時候,原來是門店準備一下原材料即可。隨著量的增多,原材料也非常多了,門店也需要吧這一部分交給“工廠”去做。
思考:原料有一系列種類,可能會有很多工廠,便於管理,抽象一個頂層工廠介面,用於定義提供原材料的介面,使用原材料工廠的pizza門店依賴頂層介面,便於管理,原材料工廠。門店依舊使用工廠方法的模式來讓子類決定具體使用哪個工廠創造哪些原材料物件。
/** * Project <demo-project> * Created by jorgezhong on 2018/9/21 10:01. * * 頂層原材料工廠: *這裡定義介面其實是使用了組合的思想,使用方法來組合例項化的原材料物件,其子類實現這些方法,例項化當地的具體產品需要的的原材料 */ public interface PizzaIngredientFactory { Dough createDough(); Sauce createSauce(); Cheese createCheese(); Veggies[] createVeggies(); Pepperoni createPepperoni(); Clams createClams(); }
/** * Project <demo-project> * Created by jorgezhong on 2018/9/21 10:10. * * 具體工廠 */ public class NYPizzaIngredientFactory implements PizzaIngredientFactory { @Override public Dough createDough() { return new ThinCrustDough(); } @Override public Sauce createSauce() { return new MarinaraSauce(); } @Override public Cheese createCheese() { return new ReggianoCheese(); } @Override public Veggies[] createVeggies() { return new Veggies[]{new Garlic(), new Onion(), new Mushroom(), new RedPepper()}; } @Override public Pepperoni createPepperoni() { return new SlicedPepperoni(); } @Override public Clams createClams() { return new FreshClams(); } }
/** * Project <demo-project> * Created by jorgezhong on 2018/9/21 10:31. * * 頂層產品抽象類:pizza,依賴一系列原材料種類 */ public abstract class Pizza { String name; Dough dough; Sauce sauce; Veggies veggies[]; Cheese cheese; Pepperoni pepperoni; Clams clams; public abstract void prepare(); public void bake(){ System.out.println("bake for 25 minutes at 350"); } public void cut() { System.out.println("cutting the pizza into diagonal slices"); } public void box() { System.out.println("place pizza in official PizzaStore box"); } public String getName() { return name; } public Pizza setName(String name) { this.name = name; return this; } @Override public String toString() { final StringBuilder sb = new StringBuilder("Pizza{"); sb.append("name='").append(name).append('\''); sb.append(", dough=").append(dough); sb.append(", sauce=").append(sauce); sb.append(", veggies=").append(Arrays.toString(veggies)); sb.append(", cheese=").append(cheese); sb.append(", pepperoni=").append(pepperoni); sb.append(", clams=").append(clams); sb.append('}'); return sb.toString(); } }
/** * Project <demo-project> * Created by jorgezhong on 2018/9/21 11:24. * * 具體產品(product)的子介面決定使用什麼工廠,並且組合哪些原材料 */ public class ClamPizza extends Pizza { PizzaIngredientFactory pizzaIngredientFactory; public ClamPizza(PizzaIngredientFactory pizzaIngredientFactory) { this.pizzaIngredientFactory = pizzaIngredientFactory; } @Override public void prepare() { System.out.println("preparing " + name); //跟工廠要原材料 dough = pizzaIngredientFactory.createDough(); sauce = pizzaIngredientFactory.createSauce(); cheese = pizzaIngredientFactory.createCheese(); } }
/** * Project <demo-project> * Created by jorgezhong on 2018/9/20 9:01. */ public abstract class PizzaStore { public Pizza orderPizza(String type) { //工廠生產需要的pizza Pizza pizza = createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } /** * 抽象工廠方法 * * @param type * @return */ abstract Pizza createPizza(String type); }
/** * Project <demo-project> * Created by jorgezhong on 2018/9/20 10:23. * 注意:使用了工廠方法,不同的是,型別的判斷原來是放在工廠裡面做的,現在放到了門店裡面族,工廠只負責組合具體物件即可 */ public class NYStylePizzaStore extends PizzaStore { @Override Pizza createPizza(String type) { Pizza pizza = null; NYPizzaIngredientFactory factory = new NYPizzaIngredientFactory(); if ("cheese".equals(type)) { pizza = new CheesePizza(factory); pizza.setName("New York Style Cheese Pizza"); } else if ("pepperoni".equals(type)) { pizza = new PepperoniPizza(factory); pizza.setName("New York Style Pepperoni Pizza"); } else if ("clam".equals(type)) { pizza = new ClamPizza(factory); pizza.setName("New York Style clam Pizza"); } else if ("veggie".equals(type)) { pizza = new VeggiePizza(factory); pizza.setName("New York Style veggie Pizza"); } return pizza; } }
測試:
public class Test { public static void main(String[] args) { NYStylePizzaStore nyStylePizzaStore = new NYStylePizzaStore(); Pizza cheese = nyStylePizzaStore.orderPizza("cheese"); } }
結果:

備註:抽象工廠的案例省略了芝加哥具體工廠類,其他具體的pizza產品類,所有原材料類,以及芝加哥門店類。大家可根據下面畫出的uml類圖喝依賴關係圖自行補充哦,實在太多了,因此不重要的就不貼出來了

抽象工廠關係圖
備註:依賴關係比較多,圖比較大,只能點開來才看的清楚啦

抽象工廠依賴圖
總結:
面向抽象,不面向具體的設計原則