摘要:在之前內容里,介紹了工廠模式中的簡單工廠和工廠方法內容,這我們繼續工廠模式的學習,今天學習抽象工廠模式。這樣的工廠形式就是抽象工廠模式抽象工廠模式提供一個接口,用于創建相關或依賴對象,而不需要明確指定具體類。
在之前內容里,介紹了工廠模式中的簡單工廠和工廠方法內容,這我們繼續工廠模式的學習,今天學習抽象工廠模式。
當直接實例化一個對象時,就是在依賴它的具體類。看一張對象依賴圖
很明顯PizzaStore依賴所有披薩對象,所有的具體pizza都從PizzaStore當中產生。
當下面各個新增一種披薩種類,就相當于多了一個依賴,上面PizzaStore可能就要修改。
這里引出一個原則:依賴倒置原則。
注:要依賴抽象,不要依賴具體類。感覺有點像面向接口編程,而不面向實現編程。確實這很相似,然而這里更強調“抽象”.這個原則說明:不能讓“高層組件”依賴“底層組件”,換句話說好比人要吃雞鴨魚肉等食物攝取營養,但是人不吃雞鴨魚肉也能從別的許多食物的攝取營養物質的把。所以二者不應該是依賴的關系,回過來說,“高層組件”和“底層組件”都要依賴抽象。
PizzaStore是“高層組件”,而各個披薩具體實現PizzaStore的具體實現類,就為“底層組件”,很明白,PizzaStore依賴這些具體比薩類。
我們把依賴倒置原則,放在我們目前的披薩店項目中。類圖如下
想要遵循依賴倒置原則,(依賴抽象)工廠方式并不一定是最好的方法,但卻是最有效方法之一。
何為“倒置”?
前面說“依賴”,現在來解釋解釋倒置原則,在上面當中我們看到各個pizza具體類(低層組件)向上指向了Pizza(高層組件),底層組件現在竟然依賴高層組件,對比圖一當中對象(向下指向)依賴具體實現類情況,是不是指向都反過來了。所以說倒置指的就是和一般OO設計的思考方式完全相反。而現在我們的設計就讓高層和底層組件都依賴Pizza這個抽象。
所以說若要遵循依賴倒置原則的話必須遵守下面幾點要求。
1:變量不可以持有具體類的引用。
注:如果使用new,就會持有具體類的引用。你可以使用工廠模式避免這個問題。
2:不要讓類派生自具體類。
注:如果派生自具體類,就會依賴這個具體類。請派生自一個抽象
3:不要覆蓋基類中已經實現的方法。
編者注:包括我之前做的幾期模式文章,當學習到工廠模式的時候我也有腦漿糊時候,倘若真的寫項目程序之前都要想如何使用什么設計模式的話,恐怕我什么程序都寫不出來,在設計模式中,無論有多少原則,其實是要盡量去達到,而不是說隨時都要遵守原則或模式。其實很多時候任何Java程序在某種程度上來說都做不到百分之百遵守原則,都有違反方針地方,但設計模式可以作為一個項目的靈魂。
但是,如果你深入體驗模式,將這些模式內化成你思考的一部分,那么在設計時,你將知道何時有足夠的理由違反這些原則。就比方說當你實例化一個字符串的時候,你是不是直接實例化了?其實我們平時在程序中也都不假思索的實例化字符串對象嗎?有違反這個原則嗎?有!能這樣做嗎?能!為什么?因為字符串不能改變。包括我自己目前學習設計模式都還只是學“形”,我離設計模式的“心”還有不少的距離。
回到我們披薩店,現在看看我們披薩店“生意”是蒸蒸日上,具體彈性的框架,維護拓展起來也比最初設計的強很多,雖然花的功夫不少,但往往是值得的。
某一天你接到了好幾個顧客的投訴電話,投訴的內容大體相同,某某加盟店里的食材不新鮮,甚至有媒體曝光你的某些加盟店為了降低成本,使用劣質原料。身為老板得你,必須采取一些辦法,阻止這樣的情況再次發生。
往后幾天,你親自考察了位于紐約和芝加哥的兩家加盟店,經過研究決定為確保加盟店使用高質量的原料,你打算建造一家生產原料的工廠,并將原料運到各個加盟店專供,下面是你寫的兩份菜單
現在我們建造一個工廠,來生產原料,
定義工廠接口,負責創建原料
這里有很多新類,每個原料都是一個類(接口),每個原料都有個create方法,到時候我們都要具體展開。
下面具體創建紐約原料工廠,
每個原料都重載接口,根據不同風味定制添加不同的調料,對于蔬菜(Veggies),用一個數組做返回值,這里我就自己寫死了數組,芝加哥工廠暫時不放出。
這里Pizza就需要修改了,好讓它能使用工廠生產出來的原料。
下面是創建披薩,下面以芝士披薩為例,不在像之前那樣做法了,寫出區域性話,就單單創建某某味道的披薩
下面圖將給你更好的說明(我就直接引用書中筆記了)
下面是修改加盟店代碼以紐約店為例,紐約店會用到紐約披薩原料工廠,把工廠對象傳入每個披薩里就能“獲取”對應原料。
相比于之前的createPizza方法,這個版本兩者之間有何異同呢?
我們目前做了這些。
這樣的工廠形式就是抽象工廠模式
抽象工廠模式:提供一個接口,用于創建相關或依賴對象,而不需要明確指定具體類。
下面是抽象工廠類圖
抽象方法,給我感覺是它更多運用抽象形式,簡化編碼當中復雜性。
下面是工廠方法和抽象工廠兩種模式的對比
工廠方法:
抽象工廠:
其實,無論簡單工廠,工廠方法還是抽象工廠方法,都對我們提升代碼質量有很大幫助。
要點:
1:所有的工廠都是用來封裝對象的創建
2:簡單工廠,雖然不算是設計模式,但卻是一個很好的編碼習慣,可以使客戶和具體類解耦
3:工廠方法使用繼承:把對象的創建放在子類,子類實現工廠方法。
4:抽象工廠模式使用對象組合:對象的創建被是現在工廠的接口中。
5:所有的工廠模式都是減少程序中類之間依賴,松耦合。
6:第一次了解原來實例化可以延遲到子類中進行。
7:學習了依賴倒置原則。
8:面向抽象編程,不面向具體類編輯,盡管自己還是會犯老毛病。
下面放出實例的全部源碼,由于源碼類文件較多,先放出了項目class圖
Material_Implements包下內容
披薩工廠接口
package Factory_Interface; import Material_Interface.Cheese; import Material_Interface.Clams; import Material_Interface.Dough; import Material_Interface.Pepperoni; import Material_Interface.Sauce; import Material_Interface.Veggies; /** * 這時我們需要給披薩店送原料了 建立一家生產原料的工廠 添加各種原料 * * @author Joy * */ public interface PizzaIngredientFactory { // 面餅 public Dough createDough(); // 醬料 public Sauce createSauce(); // 芝士奶酪 public Cheese createCheese(); // 各種蔬菜,數組 public Veggies[] createVeggies(); // 香腸 public Pepperoni createPepperoni(); // 海鮮 public Clams createClam(); }
兩個具體工廠實現
package Factory_Implements; import Factory_Interface.PizzaIngredientFactory; import Material_Implements.FreshClams; import Material_Implements.Garlic; import Material_Implements.MarinaraSauce; import Material_Implements.Mushroom; import Material_Implements.Onion; import Material_Implements.RedPepper; import Material_Implements.ReggianoCheese; import Material_Implements.SlicedPepperoni; import Material_Implements.ThinCrustDough; import Material_Interface.Cheese; import Material_Interface.Clams; import Material_Interface.Dough; import Material_Interface.Pepperoni; import Material_Interface.Sauce; import Material_Interface.Veggies; /** * 紐約披薩店原料工廠實現,工廠專精大蒜番茄醬料,干酪,鮮蛤蠣 * * @author Joy * */ // 具體的原料工廠都要實現PizzaIngredientFactory類 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() { Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() }; return veggies; } @Override public Pepperoni createPepperoni() { return new SlicedPepperoni(); } @Override public Clams createClam() { return new FreshClams(); } }
package Factory_Implements; import Factory_Interface.PizzaIngredientFactory; import Material_Implements.BlackOlives; import Material_Implements.Eggplant; import Material_Implements.FrozenClams; import Material_Implements.MozzarellaCheese; import Material_Implements.PlumTomatoSauce; import Material_Implements.SlicedPepperoni; import Material_Implements.Spinach; import Material_Implements.ThickCrustDough; import Material_Interface.Cheese; import Material_Interface.Clams; import Material_Interface.Dough; import Material_Interface.Pepperoni; import Material_Interface.Sauce; import Material_Interface.Veggies; /** * 加州披薩店原料工廠 * * @author Joy * */ public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory { @Override public Dough createDough() { return new ThickCrustDough(); } @Override public Sauce createSauce() { return new PlumTomatoSauce(); } @Override public Cheese createCheese() { return new MozzarellaCheese(); } @Override public Veggies[] createVeggies() { Veggies veggies[] = { new BlackOlives(), new Spinach(), new Eggplant() }; return veggies; } @Override public Pepperoni createPepperoni() { return new SlicedPepperoni(); } @Override public Clams createClam() { return new FrozenClams(); } }
各個原料接口
package Material_Interface; /** * 芝士奶酪類 * @author Joy * */ public interface Cheese { //只需定義接口,具體實現讓實現類搞定 public String toString(); }
package Material_Interface; /** * 海蠣類 * @author Joy * */ public interface Clams { public String toString(); }
package Material_Interface; /** * 面餅類 * @author Joy * */ public interface Dough { public String toString(); }
package Material_Interface; /** * 香腸類 * @author Joy * */ public interface Pepperoni { public String toString(); }
package Material_Interface; /** * 醬料類 * @author Joy * */ public interface Sauce { public String toString(); }
package Material_Interface; /** * 蔬菜類 * @author Joy * */ public interface Veggies { public String toString(); }
下面就是各個原料的具體實現
package Material_Implements; import Material_Interface.Veggies; public class BlackOlives implements Veggies { public String toString() { return "黑橄欖"; } }
package Material_Implements; import Material_Interface.Veggies; public class Eggplant implements Veggies { public String toString() { return "茄子"; } }
package Material_Implements; import Material_Interface.Clams; public class FreshClams implements Clams { public String toString() { return "鮮海蠣"; } }
package Material_Implements; import Material_Interface.Clams; public class FrozenClams implements Clams { public String toString() { return "海灣的鮮海蠣"; } }
package Material_Implements; import Material_Interface.Veggies; public class Garlic implements Veggies { public String toString() { return "大蒜"; } }
package Material_Implements; import Material_Interface.Sauce; public class MarinaraSauce implements Sauce { @Override public String toString() { return "番茄醬"; } }
package Material_Implements; import Material_Interface.Cheese; public class MozzarellaCheese implements Cheese { @Override public String toString() { return "碎芝士"; } }
package Material_Implements; import Material_Interface.Veggies; public class Mushroom implements Veggies { public String toString() { return "香菇"; } }
package Material_Implements; import Material_Interface.Veggies; public class Onion implements Veggies { public String toString() { return "洋蔥"; } }
package Material_Implements; import Material_Interface.Sauce; public class PlumTomatoSauce implements Sauce { public String toString() { return "梅子番茄醬"; } }
package Material_Implements; import Material_Interface.Veggies; public class RedPepper implements Veggies { public String toString() { return "紅辣椒"; } }
package Material_Implements; import Material_Interface.Cheese; public class ReggianoCheese implements Cheese { public String toString() { return "干奶酪"; } }
package Material_Implements; import Material_Interface.Pepperoni; public class SlicedPepperoni implements Pepperoni { public String toString() { return "香腸切片"; } }
package Material_Implements; import Material_Interface.Veggies; public class Spinach implements Veggies { public String toString() { return "菠菜"; } }
package Material_Implements; import Material_Interface.Dough; public class ThickCrustDough implements Dough { public String toString() { return "厚面餅"; } }
package Material_Implements; import Material_Interface.Dough; public class ThinCrustDough implements Dough { public String toString() { return "薄面餅"; } }
下面是Pizza“工廠”(抽象)方法
package Pizza; import Material_Interface.Cheese; import Material_Interface.Clams; import Material_Interface.Dough; import Material_Interface.Pepperoni; import Material_Interface.Sauce; import Material_Interface.Veggies; public abstract class Pizza { String name;// 披薩名稱 Dough dough;// 面團類型 Sauce sauce;// 醬料 // ArrayList toppings = new ArrayList();// 一套佐料(下面原料代替了list) // 每個披薩都持有一組需用到的原料 Veggies veggies[]; Cheese cheese; Pepperoni pepperoni; Clams clams; public void setName(String name) { this.name = name; } public String getName() { return name; } /** * //把prepare方法抽象化,獲取來自原料工廠的原料 Pizza類完全不關心具體原料是什么,只知道如何制作披薩, * 比薩和原料之間被解耦,往后拓展不同口味披薩,建立不同的披薩工廠也很方便 Pizza類被復用 */ // 準備 public abstract void prepare(); // 烘烤 public void bake() { System.out.println(" " + "披薩正在烘烤,需烘烤25分鐘。。。"); } // 切片 public void cut() { System.out.println("制作完成,給披薩切片。。。"); } // 裝盒 public void box() { System.out.println("給披薩打包裝盒。。。"); } // 輸出客人點的披薩信息 @Override public String toString() { StringBuffer display = new StringBuffer(); display.append(name + " "); if (dough != null) { display.append(dough + " "); } if (sauce != null) { display.append(sauce + " "); } if (cheese != null) { display.append(cheese + " "); } if (veggies != null) { for (int i = 0; i < veggies.length; i++) { display.append(veggies[i] + ", "); } } if (pepperoni != null) { display.append(pepperoni + " "); } if (clams != null) { display.append(clams + " "); } return display.toString(); } }
下面是具體Pizza實現
package Pizza; import Factory_Interface.PizzaIngredientFactory; //芝士披薩 public class CheesePizza extends Pizza { PizzaIngredientFactory ingredientFactory; // 要制作披薩,要先從對于工廠獲取原料,所以構造器初始化數據 public CheesePizza(PizzaIngredientFactory ingredientFactory) { this.ingredientFactory = ingredientFactory; } // 這里就不在以前Pizza類直接寫死,而是更加靈活使用 // prepare()方法一步步創建芝士披薩,需要的原料全部從“工廠”中獲取 @Override public void prepare() { System.out.println("準備 " + name); dough = ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); cheese = ingredientFactory.createCheese(); } }
package Pizza; import Factory_Interface.PizzaIngredientFactory; //海蠣披薩 public class ClamPizza extends Pizza { PizzaIngredientFactory ingredientFactory; public ClamPizza(PizzaIngredientFactory ingredientFactory) { this.ingredientFactory = ingredientFactory; } public void prepare() { System.out.println("準備 " + name); dough = ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); cheese = ingredientFactory.createCheese(); clams = ingredientFactory.createClam(); } }
package Pizza; import Factory_Interface.PizzaIngredientFactory; //香腸披薩 public class PepperoniPizza extends Pizza { PizzaIngredientFactory ingredientFactory; public PepperoniPizza(PizzaIngredientFactory ingredientFactory) { this.ingredientFactory = ingredientFactory; } public void prepare() { System.out.println("準備 " + name); dough = ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); cheese = ingredientFactory.createCheese(); veggies = ingredientFactory.createVeggies(); pepperoni = ingredientFactory.createPepperoni(); } }
package Pizza; import Factory_Interface.PizzaIngredientFactory; //素食披薩 public class VeggiePizza extends Pizza { PizzaIngredientFactory ingredientFactory; public VeggiePizza(PizzaIngredientFactory ingredientFactory) { this.ingredientFactory = ingredientFactory; } public void prepare() { System.out.println("準備 " + name); dough = ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); cheese = ingredientFactory.createCheese(); veggies = ingredientFactory.createVeggies(); } }
下面是PizzaStore接口
package Store; import Pizza.Pizza; /** * PizzaStore相當于客戶, 往后會有很多類似此類進行拓展 * * 在這里orderPizza()方法對pizz對象做了很多事(createPizza,prepare,bake,cut,box) * 但是由于Pizza對象是抽象的(接口), * orderPizza()并不知道具體哪些實際類參與進來了(相當于實現Pizza接口的實現類有哪些orderPizza()方法并不知道) * 換句話說,這就是解耦,減少相互依賴 * * 同理createPizza()方法中也不知道創建的具體是什么披薩 只知道披薩可以被準備,被烘烤,被切片,被裝盒行為而已 * */ // 披薩店 public abstract class PizzaStore { // 點單 public Pizza orderPizza(String type) { // createPizza()方法從工廠對象中取出, // 并且方法也不再這里實現,定義為抽象類,誰需要在拿走方法去具體實現 Pizza pizza = createPizza(type); System.out.print("----制作一個" + pizza); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } // 把工廠對象移到這個方法里,并封裝 // 現在工廠方法為抽象的,子類必須實現這個抽象方法 public abstract Pizza createPizza(String item); }
下面是兩個加盟店的具體實現
package Store; import Factory_Implements.NYPizzaIngredientFactory; import Factory_Interface.PizzaIngredientFactory; import Pizza.CheesePizza; import Pizza.ClamPizza; import Pizza.PepperoniPizza; import Pizza.Pizza; import Pizza.VeggiePizza; /** * 此時NYStylePizzaStore所封裝的知識是關于如何制作紐約風格的比薩 * * @author Joy * */ // 紐約風格披薩店(分店) public class NYStylePizzaStore extends PizzaStore { /** * 相比之前寫的這里添加了工廠類 */ // 根據披薩的不同,創建不同披薩(具體實現類) @Override public Pizza createPizza(String item) { Pizza pizza = null; // 這里多態創建紐約原料工廠,該工廠負責紐約風味的披薩 PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory(); if (item.equals("芝士")) { // 實例化具體披薩類型,并傳進該比薩所需要的工廠,以便取得原料 pizza = new CheesePizza(ingredientFactory); pizza.setName("紐約芝士披薩"); } else if (item.equals("素食")) { pizza = new VeggiePizza(ingredientFactory); pizza.setName("紐約素食披薩"); } else if (item.equals("海蠣")) { pizza = new ClamPizza(ingredientFactory); pizza.setName("紐約海蠣披薩"); } else if (item.equals("香腸")) { pizza = new PepperoniPizza(ingredientFactory); pizza.setName("紐約香腸披薩"); } return pizza; } }
package Store; import Factory_Implements.ChicagoPizzaIngredientFactory; import Factory_Interface.PizzaIngredientFactory; import Pizza.CheesePizza; import Pizza.ClamPizza; import Pizza.PepperoniPizza; import Pizza.Pizza; import Pizza.VeggiePizza; /** * 此時ChicagoStylePizzaStore所封裝的知識是關于如何制作芝加哥風格的比薩 * * @author Joy * */ // 芝加哥披薩店(分店) public class ChicagoStylePizzaStore extends PizzaStore { Pizza pizza = null; // 這里多態創建紐約原料工廠,該工廠負責紐約風味的披薩 PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory(); @Override public Pizza createPizza(String item) { if (item.equals("芝士")) { pizza = new CheesePizza(ingredientFactory); pizza.setName("芝加哥芝士披薩"); } else if (item.equals("素食")) { pizza = new VeggiePizza(ingredientFactory); pizza.setName("芝加哥素食披薩"); } else if (item.equals("海鮮")) { pizza = new ClamPizza(ingredientFactory); pizza.setName("芝加哥海蠣披薩"); } else if (item.equals("香腸")) { pizza = new PepperoniPizza(ingredientFactory); pizza.setName("芝加哥香腸披薩"); } return pizza; } }
測試類
package TestMain; import Pizza.Pizza; import Store.ChicagoStylePizzaStore; import Store.NYStylePizzaStore; import Store.PizzaStore; public class TestMain { public static void main(String[] args) { PizzaStore nyStore = new NYStylePizzaStore(); PizzaStore chicagoStore = new ChicagoStylePizzaStore(); System.out.println("===================================="); Pizza pizza3 = nyStore.orderPizza("香腸"); System.out.println("Joy點了一個" + pizza3); System.out.println("===================================="); Pizza pizza4=chicagoStore.orderPizza("海鮮"); System.out.println("Joy點了一個" + pizza4); } }
效果圖:
工廠模式給我最大感受就是對編寫好的代碼的理解程度加深,想法也更加考慮拓展了,當然還需反復多練習,模擬代碼已經全部放出,注釋也寫的比較清楚,若有不理解歡迎留言
感謝你看到這里,至此工廠模式內容結束,本人文筆隨便,若有不足或錯誤之處望給予指點,90度彎腰~~~很快我會發布下一個設計模式內容,生命不息,編程不止!
參考書籍:《Head First 設計模式》
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70195.html
摘要:對象字面量創建對象張三學生這種方式的好處顯而易見,就是解決了之前的缺點。構造函數模式張三學生李四學生與之前工廠模式的方法對比變量名首字母大寫了在函數內沒有顯式的創建及返回對象而使用了創建時使用了關鍵字。 面向對象是JS的重點與難點,但也是走向掌握JS的必經之路,有很多的文章或書籍中都對其進行了詳細的描述,本沒有必要再寫這些,但是對于學習來說,講給別人聽對自己來說是一種更好的受益方式。我...
摘要:對象字面量創建對象張三學生這種方式的好處顯而易見,就是解決了之前的缺點。構造函數模式張三學生李四學生與之前工廠模式的方法對比變量名首字母大寫了在函數內沒有顯式的創建及返回對象而使用了創建時使用了關鍵字。 面向對象是JS的重點與難點,但也是走向掌握JS的必經之路,有很多的文章或書籍中都對其進行了詳細的描述,本沒有必要再寫這些,但是對于學習來說,講給別人聽對自己來說是一種更好的受益方式。我...
摘要:設計模式的定義在面向對象軟件設計過程中針對特定問題的簡潔而優雅的解決方案。從前由于使用的局限性,和做的應用相對簡單,不被重視,就更談不上設計模式的問題。 ‘從大處著眼,從小處著手’,以前對這句話一知半解,自從踏出校門走入社會,開始工作以來,有了越來越深的理解,偶有發現這句話用在程序開發中也有用,所以,近段時間開始嘗試著分析jQuery源碼,分析angularjs源碼,學習設計模式。 設...
摘要:結構型模式適配器模式橋接模式裝飾模式組合模式外觀模式享元模式代理模式。行為型模式模版方法模式命令模式迭代器模式觀察者模式中介者模式備忘錄模式解釋器模式模式狀態模式策略模式職責鏈模式責任鏈模式訪問者模式。 主要版本 更新時間 備注 v1.0 2015-08-01 首次發布 v1.1 2018-03-12 增加新技術知識、完善知識體系 v2.0 2019-02-19 結構...
閱讀 3064·2021-10-12 10:20
閱讀 2809·2021-09-27 13:56
閱讀 790·2021-09-27 13:36
閱讀 1424·2021-09-26 09:46
閱讀 2417·2019-08-30 14:02
閱讀 2685·2019-08-28 18:14
閱讀 1257·2019-08-26 10:32
閱讀 1700·2019-08-23 18:25