摘要:與以往的使用的方式不同,工廠模式使用工廠實例化對象。抽象工廠模式亮相抽象工廠模式抽象工廠模式提供一個接口,用于創建相關或依賴對象的家族,而不需要明確指定具體類。
寫在前面
這篇博文介紹設計模式的形式將與其他篇博文不太一樣,這里我們將從一個問題入手,逐步了解到簡單工廠、工廠方法與抽象工廠模式。
PS:這篇博文涉及的內容較多,所以篇幅有點長,請耐心閱讀。
為什么要使用工廠模式?
在 OO 設計中,有一個重要的設計原則:針對接口編程而不針對實現編程。每當我們使用 new 去實例化一個對象時,用到的就是實現編程,而不是接口。這樣以來代碼綁定著具體類,會導致代碼更脆弱,缺乏彈性。
在技術上,使用 new 沒有錯,畢竟這是 Java 的基礎部分。真正錯的是“改變”,以及它會影響 new 的使用。針對接口編程,可以隔離掉以后系統可能發生的一堆改變。原因是,如果代碼針對接口編程,那么通過多態,它可以與任何新類實現該接口。
與以往的使用 new 的方式不同,工廠模式使用“工廠”實例化對象。這樣一來代碼的擴展性變得更強,又可以降低代碼之間的耦合。
一、從問題中引出工廠模式1.1 問題描述
比薩店:假設你有一家比薩店,為了吸引更多的顧客,所以你們的比薩店提供了很多種類型的比薩,比如奶酪比薩,希臘比薩等。針對這個問題設計比薩從生產到售賣相關的類。
設計圖
1.2 代碼實現
比薩類 Pizza
package com.jas.simplefactory; public abstract class Pizza { public String name; public String sauce; public void prepare(){ System.out.println("準備 ..." + name); System.out.println("添加配料 ..." + sauce); } public void bake(){ System.out.println("烘烤 25 分鐘。"); } public void cut(){ System.out.println("把比薩餅切成對角片。"); } public void box(){ System.out.println("將比薩放到比薩商店的盒子中。"); } }
奶酪比薩類 CheesePizza
package com.jas.simplefactory; public class CheesePizza extends Pizza { public CheesePizza(){ name = "奶酪比薩"; sauce = "大蒜番茄醬"; } }
希臘比薩類 GreekPizza
package com.jas.simplefactory; public class GreekPizza extends Pizza{ public GreekPizza(){ name = "希臘比薩"; sauce = "大蒜番茄醬"; } }
比薩商店類 PizzaStore
package com.jas.simplefactory; public class PizzaStore { public Pizza orderPizza(String type){ Pizza pizza = null; //比薩商店負責生產比薩 if("cheese".equals(type)){ pizza = new CheesePizza(); }else if("greek".equals(type)){ pizza = new GreekPizza(); } pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
測試類 PizzaTestDrive
package com.jas.simplefactory; public class PizzaTestDrive { public static void main(String[] args) { PizzaStore pizzaStore = new PizzaStore(); // 點一份奶酪比薩 pizzaStore.orderPizza("cheese"); } } /** * 輸出 * * 準備 ...奶酪比薩 * 添加配料 ...大蒜番茄醬 * 烘烤 25 分鐘。 * 把比薩餅切成對角片。 * 將比薩放到比薩商店的盒子中。 * */
1.3 設計引發的問題
上面代碼中我們只列出了兩種類型的比薩,在實際生產的過程中肯定不止這兩種比薩。為了解決這個問題我們可以在 PizzaStore 類中的 orderPizza() 方法中添加類型判斷,從而生產不同類型的比薩。所以,隨著比薩類型的增多,這個方法會不停的進行判斷。
假如某個比薩的銷量不好,我們不想再生產這種類型的比薩,那么我們必須要找到 PizzaStore 類中的 orderPizza() 方法,將該種類型的比薩刪除。
所以我們總結出:比薩的類型是多變的。這時候我們就要重新考慮設計的方式,簡單工廠模式就可以幫我們解決這個問題。它將一些容易改變的代碼抽取出來,多帶帶封裝。
正好符合我們的一個設計原則:找出應用中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的代碼混在一起。
二、工廠模式登場2.1 簡單工廠模式亮相
(1)簡單工廠模式
在《Head First 設計模式》一書中這樣描述簡單工廠:簡單工廠其實并不是一個設計模式,反而比較像一種編程習慣。但是由于經常被使用,所以我們給它一個“Head First Pattern 榮譽獎”。
(2)簡單工廠組成結構
工廠類角色:含有一定的商業邏輯和判斷邏輯,用來創建產品。
抽象產品角色:它一般是具體產品繼承的父類或者實現的接口。
具體產品角色:工廠類所創建的對象就是此角色的實例。
(3)簡單工廠設計圖
(4)簡單工廠代碼實現
工廠類 SimplePizzaFactory
package com.jas.simplefactory; public class SimplePizzaFactory { private SimplePizzaFactory(){}; public static Pizza createPizzs(String type){ Pizza pizza = null; if("cheese".equals(type)){ pizza = new CheesePizza(); }else if("greek".equals(type)){ pizza = new GreekPizza(); } pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
改寫比薩商店類 PizzaStore
package com.jas.simplefactory; public class PizzaStore { public Pizza orderPizza(String type){ Pizza pizza = null; pizza = SimplePizzaFactory.createPizzs(type); return pizza; } }
測試類 PizzaTestDrive
package com.jas.simplefactory; public class PizzaTestDrive { public static void main(String[] args) { PizzaStore pizzaStore = new PizzaStore(); //點一份希臘比薩 pizzaStore.orderPizza("greek"); } } /** * 輸出 * * 準備 ...希臘比薩 * 添加配料 ...大蒜番茄醬 * 烘烤 25 分鐘。 * 把比薩餅切成對角片。 * 將比薩放到比薩商店的盒子中。 * */
(5) 簡單工廠總結
使用簡單工廠模式,我們將 PizzaStroe 中易改變的代碼抽取出來單,獨用一個工廠類封裝起來。當我們想要增加或減少比薩種類時,我們不必在去修改 PizzaStroe 類,直接修改 SimplePizzaFactory 類中的代碼即可。
這樣以來代碼變得更靈活了,代碼的復用性變得更強。當其他類中也需要 Pizza 實例時,也可以直接從 SimplePizzaFactory 類中獲得。
2.2 工廠方法模式亮相
(1)工廠方法模式
工廠方法模式定義了一個創建對象的接口,但由子類決定要實例化的類是哪一個。工廠方法讓類把實例化推遲到子類。
(2)工廠方法模式組成結構
抽象工廠角色:是具體工廠角色必須實現的接口或者必須繼承的父類。在 java 中它由抽象類或者接口來實現。聲明了抽象的工廠方法。
具體工廠角色:它含有和具體業務邏輯有關的代碼。由應用程序調用以創建對應的具體產品的對象。實現了父類或接口中定義的抽象工廠方法。
抽象產品角色:它是具體產品繼承的父類或者是實現的接口。在 java 中一般由抽象類或者接口來實現。
具體產品角色:具體工廠角色所創建的對象就是此角色的實例。在 java 中由具體的類來實現。
(3)工廠方法模式的 UML 圖解
(4) 比薩店連鎖啦
由于比薩店的銷量比較好,你掙了一大筆錢,因此你想開幾家分店。最終你選擇在北京和上海各開一家分店。但是由于地區的差異,北京和上海兩地的口味也不一樣。
北京人希望比薩的調料會多一些,而上海人則希望比薩的調料少一些。所以根據口味的不同,你要制造不同口味的比薩來滿足顧客。
我們已經有了一個簡單的比薩生產工廠 SimplePizzaFactory,但是這個工廠加工的比薩并不對北京和上海兩地人的胃口。于是就想著再建兩個比薩生產工廠 (BJPizzaFactory 和 SHPizzaFactory),分別生產不同口味的比薩。
但是這樣做有一個不好的地方,雖然不同的比薩生產工廠能夠生產出對應的比薩,但是其他的方法比如:烘焙,切片和包裝卻使用著自己的生產流程 (生產一致則存在大量重復代碼)。但是你希望這些生產流程在每個工廠都應該一樣 (復用烘焙,切片和包裝的代碼,或者覆蓋它們),使用簡單工廠的方式就不再能夠行的通了。
于是工廠方法就站了出來。
(5)工廠方法模式設計圖
(6)工廠方法模式代碼實現
代碼用了上面的 Pizza 類。
抽象工廠角色 PizzaStore 抽象類
package com.jas.factorymethod; import com.jas.simplefactory.Pizza; public abstract class PizzaStore { public Pizza orderPizza(String type){ Pizza pizza = null; pizza = createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } /** * 工廠方法,將生產比薩的方法定義成抽象方法,讓子類去實現 * @param type 比薩的類型 * @return 對應的比薩實例 */ protected abstract Pizza createPizza(String type); }
具體工廠角色 BJPizzaStore 類 (這里為了防止篇幅過長,省略了 SHPizzaStore 類和對應的比薩類)
package com.jas.factorymethod; import com.jas.simplefactory.Pizza; public class BJPizzaStore extends PizzaStore { @Override protected Pizza createPizza(String type) { Pizza pizza = null; if("cheese".equals(type)){ pizza = new BJCheesePizza(); }else if("greek".equals(type)){ pizza = new BJGreekPizza(); } return pizza; } }
具體產品角色 BJCheesePizza 類 (這里只定義一種比薩的種類)
package com.jas.factorymethod; import com.jas.simplefactory.Pizza; public class BJCheesePizza extends Pizza { public BJCheesePizza(){ name = "北京人喜歡吃的奶酪比薩"; sauce = "濃濃的大蒜番茄醬"; } /** * 覆蓋 curt() 方法將比薩切成塊狀 */ @Override public void cut(){ System.out.println("把比薩餅切成方塊狀。"); } }
測試類
package com.jas.factorymethod; public class PizzaTestDrive { public static void main(String[] args) { PizzaStore pizzaStore = new BJPizzaStore(); //根據北京人的口味要一個奶酪比薩 pizzaStore.orderPizza("greek"); } } /** * 輸出 * * 準備 ...北京人喜歡吃的希臘比薩 * 添加配料 ...濃濃的大蒜番茄醬 * 烘烤 25 分鐘。 * 把比薩餅切成方塊狀。 * 將比薩放到比薩商店的盒子中。 * */
(7)工廠方法模式總結
簡單工廠模式中將生產比薩的代碼多帶帶抽取了出來,用一個工廠進行封裝,由該工廠生產比薩實例。但是由于開了比薩連鎖店,建立多個比薩工廠并不是好的解決辦法。
所以我們使用了工廠方法模式,在比薩 PizzaStore 抽象類中定義了一個抽象方法 createPizza() 方法,由其子類去實現該方法,這樣以來不同的連鎖店就能生產自己的比薩了。
如果增加產品或者改變產品的實現,PizzaStore 并不需要做任何的改變。以很好的方式實現了 PizzaStore 與具體比薩之間的解耦。
2.3 抽象工廠模式亮相
(1) 抽象工廠模式
抽象工廠模式提供一個接口,用于創建相關或依賴對象的家族,而不需要明確指定具體類。
抽象工廠允許客戶使用抽象的接口來創建一組相關的產品,而不需要知道或關心產出的具體產品類是什么。這樣一來,客戶就從具體的產品中被解耦。
(2) 抽象工廠模式的 UML 圖解
(3)比薩店連鎖店出了一些狀況
比薩連鎖店成功的關鍵是在于新鮮、高質量的原料。但是有的連鎖店開始使用低價的原料來增加利潤,為了防止比薩店的聲譽遭到破壞,你必須采取相對應的措施。為了解決這個問題,你打算新建一家生產原料的工廠,并將原料運往各家加盟店,這樣以一來,所有的加盟店都使用你生產的原料來制作比薩。
但是這樣做卻有一個問題:因為加盟店坐落于不同的地域,比如北京和上海,它們需要的醬料是不一樣的。所以為了滿足需求,你為它們準備了兩組不同的原料。
于是你有了一個想法,你打算創建一個原料接口,由不同地域的工廠自己去實現。
(4)抽象工廠模式設計圖
(5) 抽象工廠模式代碼實現
抽象原料工廠 PizzaInfgredientFactory 接口
package com.jas.abstractfactory; public interface PizzaInfgredientFactory { Dough createDough(); Cheese createCheese(); }
具體原料工廠 SHPizzaInfgredientFactory 類
package com.jas.abstractfactory; /** * 這里是上海原料加工廠,生產的原料是薄面團與 Reggiano 干酪 */ public class SHPizzaInfgredientFactory implements PizzaInfgredientFactory { @Override public Dough createDough() { return new ThinCrustDough(); } @Override public Cheese createCheese() { return new ReggianoCheese(); } }
抽象 Pizza 類
package com.jas.abstractfactory; public abstract class Pizza { public String name; //為了篇幅過長,不再貼出原料相關代碼 public Dough dough; public Cheese cheese; public abstract void prepare(); public void bake(){ System.out.println("烘烤 25 分鐘。"); } public void cut(){ System.out.println("把比薩餅切成對角片。"); } public void box(){ System.out.println("將比薩放到比薩商店的盒子中。"); } }
具體 CheesePizza 類 (這里只新建了一個比薩具體實現類)
package com.jas.abstractfactory; public class CheesePizza extends Pizza { PizzaInfgredientFactory infgredientFactory = null; public CheesePizza(PizzaInfgredientFactory infgredientFactory){ this.infgredientFactory = infgredientFactory; } @Override public void prepare() { System.out.println("準備" + name); dough = infgredientFactory.createDough(); cheese = infgredientFactory.createCheese(); } }
具體 SHPizzaStore 類
package com.jas.abstractfactory; public class SHPizzaStore { PizzaInfgredientFactory infgredientFactory = new SHPizzaInfgredientFactory(); protected Pizza createPizza(String type){ Pizza pizza = null; if("cheese".equals(type)){ //生產比薩的原料來自上海的原料加工工廠 pizza = new CheesePizza(infgredientFactory); pizza.name = "原料來自上海工廠加工的奶酪比薩"; } pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
測試類 PizzaTestDrive
package com.jas.abstractfactory; public class PizzaTestDrive { public static void main(String[] args) { SHPizzaStore pizzaStore = new SHPizzaStore(); pizzaStore.createPizza("cheese"); } } /** * 準備原料來自上海工廠加工的奶酪比薩 * 烘烤 25 分鐘。 * 把比薩餅切成對角片。 * 將比薩放到比薩商店的盒子中。 */
(6)抽象工廠模式總結
通過抽象工廠所提供的接口,可以創建產品的家族,利用這個接口書寫代碼,我們的代碼將從實際工廠解耦,以便在不同上下文中實現各式各樣的工廠,制造各樣不同的產品。
三、工廠方法模式與抽象工廠模式總結工廠方法與抽象工廠的工作都是負責創建對象,但是工廠方法使用的方法是繼承,而抽象工廠使用的是組合。
這意味著,利用工廠方法創建對象,需要擴展一個類,并實現其中的工廠方法。由這個方法創建對象,只不過這個方法通過子類創建對象,用這種做法,客戶只需要知道他們所使用的抽象類型就可以了,而由子類負責決定具體類型。所以,工廠方法只負責將客戶從具體類型中解耦。
抽象工廠提供一個類來創建一個產品家族的抽象類型,這個類型的子類定義了產品被產生的方法。要使用這個工廠,必須先要實例化它,然后將它傳入一些針對抽象類型所寫的代碼中。
所以,和工廠方法一樣,抽象工廠也可以實現客戶從所使用地實際具體產品中解耦。但是它還有另一個優點:可以把一群相關的產品集合起來創建。但是這也有一個缺點:如果新加入創建的產品,就必須要改變接口...這樣做的后果是很嚴重的。
其實抽象工廠中的具體工廠經常使用工廠方法來創建產品。總之,它們兩個都可以將對象的創建封裝起來,使應用程序解耦,因此降低程序之間的依賴。
當你需要創建產品家族和想讓制造的相關產品集合起來時,你可以使用抽象工廠。當你需要創建一種類型的對象時,你可以選擇使用工廠方法。
參考資料《Head First 設計模式》
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/77332.html
摘要:所謂的產品族,一般或多或少的都存在一定的關聯,抽象工廠模式就可以在類內部對產品族的關聯關系進行定義和描述,而不必專門引入一個新的類來進行管理。 0x01.定義與類型 定義:抽象工廠模式提供一個創建一系列相關或相互依賴對象的接口 無需指定它們具體的類 類型:創建型 UML showImg(https://segmentfault.com/img/bVbtBp1?w=800&h=862...
摘要:通過工廠方法模式的類圖可以看到,工廠方法模式有四個要素工廠接口工廠接口是工廠方法模式的核心,與調用者直接交互用來提供產品。使用場景創建類模式,在任何需要生成復雜對象的地方,都可以使用工廠方法模式。 0x01.定義與類型 定義:定義一個創建對象的接口,但讓實現這個接口的類來決定實例化那個類,工廠方法讓類的實例化推遲到子類中進行 類型:創建型 uml類圖 showImg(https:/...
摘要:類型創建型,但不屬于中設計模式。簡介通過一個專門的工廠類來創建其他類,這些被創建的類通常有一個共同的父類或接口。相關代碼簡單工廠模式推薦閱讀慕課網設計模式精講簡單工廠模式 0x01.定義與類型 定義:由一個工廠對象決定創建出哪一種產品類的實例。 類型:創建型,但不屬于GOF23中設計模式。 簡介:通過一個專門的工廠類來創建其他類,這些被創建的類通常有一個共同的父類或接口。 uml類圖...
摘要:需要說明的是在設計模式一書中將工廠模式分為兩類工廠方法模式與抽象工廠模式,將簡單工廠模式看為工廠方法模式的一種特例,兩者歸為一類。工廠模式的作用工廠模式的作用封裝變化創建邏輯有可能變化,封裝成工廠類之后,創建邏輯的變更對調用者透明。1、什么是工廠模式Define an interface for creating an object,but let subclasses decide whi...
摘要:強大的表單驗證前端掘金支持非常強大的內置表單驗證,以及。面向對象和面向過程的區別的種設計模式全解析后端掘金一設計模式的分類總體來說設計模式分為三大類創建型模式,共五種工廠方法模式抽象工廠模式單例模式建造者模式原型模式。 強大的 Angular 表單驗證 - 前端 - 掘金Angular 支持非常強大的內置表單驗證,maxlength、minlength、required 以及 patt...
摘要:工廠模式,又稱為工廠方法模式。工廠模式,也就是工廠方法模式是解決了簡單工廠要修改代碼的問題,他把對象的創建操作延遲到子類工廠中,這樣新增產品就不需要修改代碼。 簡單工廠其實并不是設計模式,只是一種編程習慣。 首先我們創建父類Cup,所有杯子類的父類。再創建它的子類BigCup和SmallCup類。 public abstract class Cup { public abst...
閱讀 1863·2023-04-26 02:46
閱讀 1995·2021-11-25 09:43
閱讀 1140·2021-09-29 09:35
閱讀 2095·2019-08-30 15:56
閱讀 3419·2019-08-30 15:54
閱讀 2627·2019-08-29 16:35
閱讀 3116·2019-08-29 15:25
閱讀 3282·2019-08-29 14:01