1. 程式人生 > >java23種設計模式——三、工廠模式

java23種設計模式——三、工廠模式

原始碼在我的[github](https://github.com/witmy/JavaDesignPattern)和[gitee](https://gitee.com/witmy/JavaDesignPattern)中獲取 # 目錄 [java23種設計模式—— 一、設計模式介紹](https://www.cnblogs.com/codermy/p/13565137.html) [java23種設計模式—— 二、單例模式](https://www.cnblogs.com/codermy/p/13566365.html) [java23種設計模式——三、工廠模式](https://www.cnblogs.com/codermy/p/13569486.html) # 工廠模式 ## 工廠模式介紹 ``` 工廠模式是我們最常用的例項化物件模式了,是用工廠方法代替new操作的一種模式。著名的Jive論壇 ,就大量使用了工廠模式,工廠模式在Java程式系統可以說是隨處可見。因為工廠模式就相當於建立例項物件的new,我們經常要根據類Class生成例項物件,如A a=new A() 工廠模式也是用來建立例項物件的,所以以後new時就要多個心眼,是否可以考慮使用工廠模式,雖然這樣做,可能多做一些工作,但會給你係統帶來更大的可擴充套件性和儘量少的修改量。(百度百科) 工廠模式又分為: ``` - 簡單工廠模式:允許介面建立物件,但不會暴露物件的建立邏輯。 - 工廠方法模式: 允許介面建立物件,但使用哪個類來建立物件,則是交由子類決定的 - 抽象方法模式: 抽象工廠是一個能夠建立一系列相關的物件而無需指定/公開其具體類的介面。該模式能夠提供其他工廠的物件,在其內部建立其他物件。 ## 簡單工廠模式 屬於**建立型模式**,又叫做**靜態工廠方法模式**,不屬於23種GOF設計模式之一。是由一個工廠物件決定創建出哪一種產品類的例項。違背“開放 - 關閉原則”,一旦新增新產品就不得不修改工廠類的邏輯,這樣就會造成工廠邏輯過於複雜。 假設現在有一家餐館 ```java public interface Restaurant { public void cook(); } ``` 餐館有兩種菜品:紅燒肉和雞蛋羹 ```java //雞蛋羹 public class Egg implements Restaurant { @Override public void cook() { System.out.println("雞蛋羹做好了"); } } ``` ```java //紅燒肉 public class Meet implements Restaurant{ @Override public void cook() { System.out.println("紅燒肉做好了"); } } ``` 餐館裡有服務員,來負責向後廚傳達客人的需求 ```java public class Waiter { //同樣可以定義常量然後通過switch語句來實現 public static Restaurant getFood(String orderType) { Restaurant restaurant = null; if(orderType.equals("紅燒肉")){ restaurant = new Meet(); }else if (orderType.equals("雞蛋羹")){ restaurant = new Egg(); } return restaurant; } } ``` 現在顧客來了,要點一份紅燒肉,就只需要和服務員說就行 ```java public class Customer { public static void main(String[] args) { Restaurant restaurant = Waiter.getFood("紅燒肉"); restaurant.cook(); } } ``` 輸出 ```bash 紅燒肉做好了 ``` 通過以上方法,的確實現了 提供建立例項的功能,而無需關心具體實現。但是我們不難發現,這種方法的擴充套件性很差,如果餐館新出了一款菜品,還需要我們在服務員方法裡修改。這使得當餐館的菜品很多時,工廠方法程式碼邏輯將會非常複雜 ## 工廠方法模式 工廠方法模式,又稱工廠模式、多型工廠模式和虛擬構造器模式,通過定義工廠父類負責定義建立物件的公共介面,而子類則負責生成具體的物件。是在工廠模式家族中是用的最多模式,一般專案中存在最多的就是這個模式。是對簡單工廠模式的一個優化,讓每個物件都有一個與之對應的工廠。 這裡我們接著用上面的例子,假設這家餐廳的生意非常好,所以餐館的老闆把餐館所有負責點餐的服務員都辭退了,取而代之的是添加了一個收銀臺,然後讓每個廚師只負責做一樣菜。這樣客人只需要和收銀臺說要求就行了。 這裡我們接著用上面的類。除去服務員Waiter類 新建Cashier介面 ```java /** * @author codermy * @createTime 2020/6/15 */ public interface Cashier { public Restaurant getFood(); } ``` 然後給每一個菜品新建一個工廠類 EggCooker ```java /** * @author codermy * @createTime 2020/6/15 */ public class EggCooker implements Cashier { @Override public Restaurant getFood() { return new Egg(); } } ``` MeetCooker ```java /** * @author codermy * @createTime 2020/6/15 */ public class MeetCooker implements Cashier{ @Override public Restaurant getFood() { return new Meet(); } } ``` 然後顧客點單隻需要在收銀臺,餐廳的系統會自動將資訊傳遞給相應的廚師,對應的廚師就能在餐館中把菜做好 ```java /** * @author codermy * @createTime 2020/6/15 * 消費者 */ public class Customer { public static void main(String[] args) { Cashier order = new EggCooker(); Restaurant food = order.getFood(); food.cook(); } } ``` 輸出結果 ```bash 雞蛋羹做好了 ``` 工廠方法模式解決了簡單工廠模式不符合的開閉原則,當新增一個菜品時,只需要再僱傭一個廚師就行(從現實角度看,老闆有點虧哦)。但是這也增加了系統的複雜度(菜越多,廚師就越多,這哪家餐館頂的住) ## 抽象工廠模式 這個模式解決了每個工廠只能建立一類產品(工廠方法模式)的問題 這裡用餐館的例子不太形象,不是很容易理解,強行舉例可能會和上面的方法弄混,我自己繞了好一會,所以我們換一個例子。 現在我們人手不離手機,我們假設手機有如下幾個功能 ```java //手機產品介面 public interface IphoneProduct { void callup();//打電話 void sendSms();//發簡訊 } ``` 每個人家裡又都有路由器,路由器有如下功能 ```java //路由器產品介面 public interface IRouterProduct { void openwifi();//開啟wifi void setting();//設定wifi } ``` 然後現在有一個抽象產品工廠,是來生產這兩樣產品的,假設生產手機和路由器的方法是一樣的,只是需要加上廠商資訊 ```java //抽象產品工廠 public interface IProductFactory { //生產手機 IphoneProduct iphoneProduct(); //生產路由器 IRouterProduct iRouterProduct(); } ``` 現在有兩家廠商,小米和華為工廠,可以生產手機和路由器,他們兩家廠商分別由兩條產業線來做手機和路由器 ```java //小米手機 public class XiaomiPhone implements IphoneProduct{ @Override public void callup() { System.out.println("用小米手機打電話"); } @Override public void sendSms() { System.out.println("用小米手機發簡訊"); } } ``` ```java //小米路由器 public class XiaomiRouter implements IRouterProduct { @Override public void openwifi() { System.out.println("開啟小米wifi"); } @Override public void setting() { System.out.println("設定小米wifi"); } } ``` ```java //小米廠商 public class XiaomiFactory implements IProductFactory { @Override public IphoneProduct iphoneProduct() { return new XiaomiPhone(); } @Override public IRouterProduct iRouterProduct() { return new XiaomiRouter(); } } ``` ```java //華為手機 public class HuaweiPhone implements IphoneProduct { @Override public void callup() { System.out.println("用華為手機打電話"); } @Override public void sendSms() { System.out.println("用華為手機發簡訊"); } } ``` ```java //華為路由器 public class HuaweiRouter implements IRouterProduct { @Override public void openwifi() { System.out.println("開啟華為wifi"); } @Override public void setting() { System.out.println("設定華為wifi"); } } ``` ```java //華為工廠 public class HuaweiFactory implements IProductFactory { @Override public IphoneProduct iphoneProduct() { return new HuaweiPhone(); } @Override public IRouterProduct iRouterProduct() { return new HuaweiRouter(); } } ``` 消費者類 ```java //消費者/測試類 public class Customer { public static void main(String[] args) { System.out.println("==============小米產品================="); XiaomiFactory xiaomiFactory = new XiaomiFactory();//新建一個小米工廠 IphoneProduct xiaomiiphoneProduct = xiaomiFactory.iphoneProduct();//小米工廠開始生產小米手機 xiaomiiphoneProduct.callup();//測試小米手機打電話功能 IRouterProduct xiaomiiRouterProduct = xiaomiFactory.iRouterProduct();//小米工廠開始生產小米路由器 xiaomiiRouterProduct.openwifi();//測試小米路由器開啟wifi功能 System.out.println("==============華為產品================="); HuaweiFactory huaweiFactory = new HuaweiFactory(); IphoneProduct huaweiiphoneProduct1 = huaweiFactory.iphoneProduct(); huaweiiphoneProduct1.callup(); IRouterProduct huaweiiRouterProduct = huaweiFactory.iRouterProduct(); huaweiiRouterProduct.openwifi(); } } ``` 輸出 ```bash ==============小米產品================= 用小米手機打電話 開啟小米wifi ==============華為產品================= 用華為手機打電話 開啟華為wifi ``` 抽象工廠模式相較於以上兩種模式難以理解一些。這裡提供另一種寫法比較好理解,來自[Guide哥的部落格](https://blog.csdn.net/qq_34337272/article/details/80472071#41-%E4%BB%8B%E7%BB%8D)(以下所有內容) 不知道大家玩過穿越火線或者吃雞這類遊戲了嗎,遊戲中存在各種槍。我們假設現在存在AK、M4A1兩類槍,每一種槍對應一種子彈。我們現在這樣考慮生產AK的工廠可以順便生產AK使用的子彈,生產M4A1的工廠可以順便生產M4A1使用的子彈。(AK工廠生產AK系列產品包括子彈啊,AK槍的型別啊這些,M4A1工廠同理) ———————————————— **(1)建立相關介面:** 槍 ```java public interface Gun { public void shooting(); } ``` 子彈 ```java public interface Bullet { public void load(); } ``` **(2)建立介面對應實現類:** AK類 ```java public class AK_Bullet implements Bullet { @Override public void load() { System.out.println("Load bullets with AK"); } } ``` M4A1類 ```JAVA public class M4A1 implements Gun { @Override public void shooting() { System.out.println("shooting with M4A1"); } } ``` AK子彈類 ```java public class AK_Bullet implements Bullet { @Override public void load() { System.out.println("Load bullets with AK"); } } ``` M4A1子彈類 ```java public class AK_Bullet implements Bullet { @Override public void load() { System.out.println("Load bullets with AK"); } } ``` **(3)建立工廠介面** ```java public class AK_Bullet implements Bullet { @Override public void load() { System.out.println("Load bullets with AK"); } } ``` **(4)建立具體工廠** 生產AK和AK子彈的工廠 ```java public class AK_Bullet implements Bullet { @Override public void load() { System.out.println("Load bullets with AK"); } } ``` 生產M4A1和M4A1子彈的工廠 ```java public class AK_Bullet implements Bullet { @Override public void load() { System.out.println("Load bullets with AK"); } } ``` **(5)測試** ```java public class AK_Bullet implements Bullet { @Override public void load() { System.out.println("Load bullets with AK"); } } ``` **輸出結果:** ```bash Load bullets with AK shooting with