先編一個這麼久不寫的理由 上周我終於鼓起勇氣翻開了headfirst設計模式這本書,看看自己下一個設計模式要寫個啥,然後,我終於知道我為啥這麼久都沒寫設計模式了,headfirst的這個抽象工廠模式,額,我看了好幾次,都不太理解。 在我的印象中,簡單工廠,工廠方法,抽象工廠,這三個東西應該是層層遞進 ...
先編一個這麼久不寫的理由
上周我終於鼓起勇氣翻開了headfirst設計模式這本書,看看自己下一個設計模式要寫個啥,然後,我終於知道我為啥這麼久都沒寫設計模式了,headfirst的這個抽象工廠模式,額,我看了好幾次,都不太理解。
在我的印象中,簡單工廠,工廠方法,抽象工廠,這三個東西應該是層層遞進的,然後我帶著這個思路去看,emmmm,真的沒看懂,還好最近又補了一遍《大話設計模式》,揣著剛剛溫習了的新知識,然後又上了headfirst這條船,我感覺我這次應該是看懂了。
所以不寫並不是因為忙,也不是因為懶,什麼上分和吃雞,我根本都沒有聽說過,和一個996的碼農說這些,表示很扎心。
看完了工廠模式以後,發現後面居然是單例模式,兄弟,你是認真的嗎?(淚流滿面)
抽象工廠舉例分析
首先,我去看了一下這本書配套的源代碼,嗯,抽象工廠的類,感覺太多太多了,帶上測試類一共是35個,這要是貼出來,可以水很大的篇幅了,但是我是那種人嗎?當然不是!
碰到這種情況當然是直接給地址,有需要的小伙伴可以自己去看
github地址:https://github.com/bethrobson/Head-First-Design-Patterns
工廠模式遞進體系分析
先捋一捋書中描述的抽象工廠是個什麼意思,要說清楚這個,就必須先說一下它整個工廠模式的敘述的遞進關係,具體如下:
1,先拿一個賊簡單的創建Pizza的簡單工廠類PizzaStore,來忽悠我,這章很簡單,來學學吧,這章源代碼只有8個類
2,興衝衝的看完了第一部分,當然是一鼓作氣的看第二部分,第二部分總結起來大概是講這麼一個事情:
1)我們公司在紐約的那個旗艦店(PizzaStore)生意不錯,現在我們要開一個分店,名字叫芝加哥披薩店(ChicagoPizzaStore)
2)分店開了以後,原來的先進經驗不能丟啊,所以PizzaStore被抽象出來了,搞成了一個抽象類,作為基礎技術儲備
3)PizzaStore變成了公司的基礎部門,專門指導披薩製作的流程
4)旗艦店沒辦法啊,一方面自己的名字被占用了,一方面規範管理的風也吹來了,就改名字叫紐約披薩店(NYPizzaStore),旗下的所有披薩也順勢把名字也改了
5)新開起來的芝加哥披薩店,倚靠了公司的技術實力,copy了一份旗艦店的菜單,但是也要照顧本地人的口味啊,所以就因地制宜,開發了自己的產品
整個事情就是這樣,涉及到的類有:pizza抽象類(1個),PizzaStore抽象類(1個),實體披薩店(2個),實體店的各類Pizza(8個),測試類(1個)
總的來說,這一部分的遞進關係很棒,也能很好的體現,簡單工廠和工廠方法最大的不同,工廠方法可以對工廠類做到:開閉原則
3,工廠方法扯完了,就來看一下,抽象工廠嘛,這一波操作我當時著實沒有看懂,就一直擱置了,為什麼當時沒看懂呢?
對於正常的抽象工廠的講解套路來說應該是這個樣子的:新增加一個產品線(抽象類,實現類),重新規劃一下Factory的介面和實現類(Factory里新加一個介面,實現類新增實現)
它沒有這樣搞,它直接根據披薩原料,重新抽象了一套抽象工廠模式的體系出來,不得不說,這個比剛纔說的那個新增產品線的例子漂亮太多了,原因有以下幾點:
1)直接新增一個產品線的這種操作,雖然更容易理解,但是會給人一個誤導:我在原來Factory新增介面的這個操作,是符合設計模式,符合抽象工廠模式的
2)抽象工廠的缺點是什麼?缺點就是,Factory的介面,在實際使用的時候,幾乎是無法新增產品的,修改太多了。所以它選擇了使用原料這套新體系來講解,更加符合實際
當然,它的例子也不是沒有缺點,個人感覺它的缺點有以下幾點:
1)產生了類太多了,35個(上面有說過),對新手不友好,看到這個量級,稍微往後一翻又是一個單例模式,想著這個東西又不怎麼能用上,有求生欲望的,都不會在放棄的邊緣瘋狂試探……
2)對它本身的工廠方法模式的體系(PizzaStore體系),也有很大量的修改,集中體現在各類Pizza實現類的縮減(取消了按店鋪名稱來創建的各類披薩,轉而使用了簡單工廠模式裡面的名字),Pizza的方法實現也有很多修改,主要是為了支持原料體系,當然,雖然產生了很多的修改,但是對外部提供的介面是沒有影響的,換句話說,雖然實現有了翻天覆地的變化,但是顧客還是無感知的,這個從測試代碼就能看出來:
工廠方法模式測試類:
public class PizzaTestDrive { public static void main(String[] args) { PizzaStore nyStore = new NYPizzaStore(); PizzaStore chicagoStore = new ChicagoPizzaStore(); Pizza pizza = nyStore.orderPizza("cheese"); System.out.println("Ethan ordered a " + pizza.getName() + "\n"); pizza = chicagoStore.orderPizza("cheese"); System.out.println("Joel ordered a " + pizza.getName() + "\n"); pizza = nyStore.orderPizza("clam"); System.out.println("Ethan ordered a " + pizza.getName() + "\n"); pizza = chicagoStore.orderPizza("clam"); System.out.println("Joel ordered a " + pizza.getName() + "\n"); pizza = nyStore.orderPizza("pepperoni"); System.out.println("Ethan ordered a " + pizza.getName() + "\n"); pizza = chicagoStore.orderPizza("pepperoni"); System.out.println("Joel ordered a " + pizza.getName() + "\n"); pizza = nyStore.orderPizza("veggie"); System.out.println("Ethan ordered a " + pizza.getName() + "\n"); pizza = chicagoStore.orderPizza("veggie"); System.out.println("Joel ordered a " + pizza.getName() + "\n"); } }View Code
抽象工廠測試類:
public class PizzaTestDrive { public static void main(String[] args) { PizzaStore nyStore = new NYPizzaStore(); PizzaStore chicagoStore = new ChicagoPizzaStore(); Pizza pizza = nyStore.orderPizza("cheese"); System.out.println("Ethan ordered a " + pizza + "\n"); pizza = chicagoStore.orderPizza("cheese"); System.out.println("Joel ordered a " + pizza + "\n"); pizza = nyStore.orderPizza("clam"); System.out.println("Ethan ordered a " + pizza + "\n"); pizza = chicagoStore.orderPizza("clam"); System.out.println("Joel ordered a " + pizza + "\n"); pizza = nyStore.orderPizza("pepperoni"); System.out.println("Ethan ordered a " + pizza + "\n"); pizza = chicagoStore.orderPizza("pepperoni"); System.out.println("Joel ordered a " + pizza + "\n"); pizza = nyStore.orderPizza("veggie"); System.out.println("Ethan ordered a " + pizza + "\n"); pizza = chicagoStore.orderPizza("veggie"); System.out.println("Joel ordered a " + pizza + "\n"); } }View Code
是一個非常贊的地方
抽象工廠實現過程
首先,我們從原料的工廠類入手:
public interface PizzaIngredientFactory { Dough createDough(); Sauce createSauce(); Cheese createCheese(); Veggies[] createVeggies(); Pepperoni createPepperoni(); Clams createClam(); }
一共有6種原料可以創建,這個也是它源代碼類多的原因之一,由於大多數代碼都是相似的,所以這裡也就不一一的列舉了只列舉流程相關的實現
先看醬油(Sauce)相關的介面和實現:
public interface Sauce { String toString(); }
PlumTomatoSauce:
public class PlumTomatoSauce implements Sauce { public String toString() { return "Tomato sauce with plum tomatoes"; } }View Code
MarinaraSauce:
public class MarinaraSauce implements Sauce { public String toString() { return "Marinara Sauce"; } }View Code
然後看看麵團的相關介面和實現:
public interface Dough { String toString(); }
ThickCrustDough:
public class ThickCrustDough implements Dough { public String toString() { return "ThickCrust style extra thick crust dough"; } }View Code
ThinCrustDough:
public class ThinCrustDough implements Dough { public String toString() { return "Thin Crust Dough"; } }View Code
以上,是抽象工廠的基礎:產品族的工廠類,產品介面(或者是抽象類),產品的具體實現類請忽略它只是為了方便理解放到一起的
列舉了這個,再來看看具體的原料工廠:
ChicagoPizzaIngredientFactory:
public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory { //我是麵團,我是這裡 public Dough createDough() { return new ThickCrustDough(); } //我是醬油,我在這裡 public Sauce createSauce() { return new PlumTomatoSauce(); } public Cheese createCheese() { return new MozzarellaCheese(); } public Veggies[] createVeggies() { Veggies veggies[] = { new BlackOlives(), new Spinach(), new Eggplant() }; return veggies; } public Pepperoni createPepperoni() { return new SlicedPepperoni(); } public Clams createClam() { return new FrozenClams(); } }View Code
NYPizzaIngredientFactory:
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(); } }View Code
實現也是很簡單的,這裡為了不占篇幅就隱藏了,有需要的自己點開看,接下來看一個這個原料體系的建立,需要修改的體現:
1,各類Pizza的實現類,需要持有PizzaIngredientFactory對象,並且prepare方法會有修改
public class ClamPizza extends Pizza { PizzaIngredientFactory ingredientFactory; public ClamPizza(PizzaIngredientFactory ingredientFactory) { this.ingredientFactory = ingredientFactory; } void prepare() { System.out.println("Preparing " + name); dough = ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); cheese = ingredientFactory.createCheese(); clam = ingredientFactory.createClam(); } }
2,具體的pizza工廠的修改:
public class ChicagoPizzaStore extends PizzaStore { protected Pizza createPizza(String item) { Pizza pizza = null; //創建各自的原料工廠 PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory(); if (item.equals("cheese")) { //創建具體pizza的時候傳入原料工廠 pizza = new CheesePizza(ingredientFactory); pizza.setName("Chicago Style Cheese Pizza"); } else if (item.equals("veggie")) { pizza = new VeggiePizza(ingredientFactory); pizza.setName("Chicago Style Veggie Pizza"); } else if (item.equals("clam")) { pizza = new ClamPizza(ingredientFactory); pizza.setName("Chicago Style Clam Pizza"); } else if (item.equals("pepperoni")) { pizza = new PepperoniPizza(ingredientFactory); pizza.setName("Chicago Style Pepperoni Pizza"); } return pizza; } }
以上就是抽象工廠類的實現套路,以及在head first中,對原工廠方法模式具體影響的敘述,貼代碼是不可能貼代碼的,這輩子都不可能貼代碼的