3.1模式關聯的故事背景
去肯德基點餐(一個麻辣雞腿漢堡、四個奧爾良烤雞翅、一包薯條、兩杯可樂)
3.2模式定義
抽象工廠模式(Abstract Factory Pattern)提供了一個接口,用於創建相關或者依賴對象的家族,而不需要指定具體的實現類。
抽象工廠模式允許客戶使用抽象接口來創建一組相關的產品,客戶類和工廠類分開,客戶需要任何產品的時候,只需要向工廠請求即可,客戶無須修改就可以獲得新產品。這樣一來,客戶就從具體產品中解耦。
3.3故事中的模式分析
3.3.1故事中的角色
肯德基店---生產食物的工廠
食物(漢堡、雞翅、薯條、可樂)---工廠生產的產品
客戶---食物購買者
以上三種角色的關系如圖3-2所示:
3.3.2抽象化分析
還記得“開-閉”原則和“依賴倒置”原則嗎?這是我們進行軟件程序設計的指導原則。我們要讓程序對擴展開放,對修改關閉,如何能做到呢?——抽象!我們要對系統模型進行最大化的抽象,才能達到“開-閉”原則的要求,又如何才能做到最大抽象呢?“依賴倒置”原則為我們指明了一條方向,從具體的類型來進行抽象。
來看下故事中出現的對象:肯德基店就是一個具體的工廠,這時,我們需要抽象一個工廠,在抽象工廠中指明了生產各種抽象食物的方法,如生產漢堡、雞翅等,肯德基店就需要實現這個抽象工廠,生產具體的食品,如生產麻辣雞腿漢堡、生產奧爾良雞翅等。前面提到了“抽象食物”,我們還需要對每個具體的食物添加抽象父類,如漢堡就是抽象父類,麻辣雞腿漢堡就是漢堡的一個子類,依次類推。這時,我們會發現,每一種食物又都存在着一些共同的屬性,如風味、單價、數量等,因此,我們繼續進行抽象,所有的抽象食物都繼承一個抽象父類。客戶如何訂購肯德基生產的食物呢?這與上一章的工廠方法模式有所不同,這里我們使用組合的方式,將抽象工廠作為客戶類中的一個實例變量,客戶需要任何產品的時候,只需要向工廠請求即可,這就是抽象工廠模式的應用方式。客戶類和工廠類分開,客戶無須修改就可以獲得新產品。
通過以上分析,對圖3-2進行抽象化改進,如圖3-3(a)所示。
看似改動很小,但是意義深遠。我們在客戶類中使用的都是抽象類和接口,這樣即使生產再多的食物都不用擔心了。
3.3.3抽象工廠模式的靜態建模
現在客戶——肯德基——食物三者之間的關系已經理順了,靜態類圖如3-3(b)所示。
圖中所表達的內容是客戶需要食物只要向抽象工廠請求即可,由具體生產具體產品給客戶。
3.4故事抽象工廠模式的實現
3.4.1抽象食物的建立
1)抽象食物——AbstractBaseFood
package com.demo.factory.model; /** * Created by lsq on 2018/3/13. * 食物基類 */ public abstract class AbstractBaseFood { //類別 protected String kind; //數量 protected int num; //價格 protected float price; //合計 public float totalPrice(){ return this.num * this.price; } }
2)食物接口——IFood
新建IFood接口,該接口存在一個printMessage方法,子類實現該方法打印輸出食物的相關信息。
package com.demo.factory.model; /** * Created by lsq on 2018/3/13. * 抽象食物接口 */ public interface IFood { /** * 打印輸出食物信息 */ void printMessage(); }
3.4.2建立不同食物的抽象基類
每種食物的抽象類都繼承AbstractBaseFood基類,實現IFood接口。每種食物的抽象基類很簡單,就是實現IFood的printMessage方法。
1)漢堡基類——Hamburg
package com.demo.factory.model; /** * Created by lsq on 2018/3/13. * 漢堡基類 */ public abstract class Hamburg extends AbstractBaseFood implements IFood{ public void printMessage(){ System.out.println("--"+this.kind+"風味漢堡,\t單價:"+this.price+ ",\t數量:"+this.num+",\t合計:"+this.totalPrice()); } }
2)雞翅基類——ChickenWings
package com.demo.factory.model; /** * Created by lsq on 2018/3/13. * 雞翅基類 */ public abstract class ChickenWings extends AbstractBaseFood implements IFood{ public void printMessage(){ System.out.println("--"+this.kind+"風味雞翅,\t單價:"+this.price+ ",\t數量:"+this.num+",\t合計:"+this.totalPrice()); } }
3)薯條基類——FrenchFries
package com.demo.factory.model; /** * Created by lsq on 2018/3/13. * 薯條基類 */ public abstract class FrenchFries extends AbstractBaseFood implements IFood{ public void printMessage(){ System.out.println("--"+this.kind+"風味薯條,\t單價:"+this.price+ ",\t數量:"+this.num+",\t合計:"+this.totalPrice()); } }
4)飲料基類——Beverage
package com.demo.factory.model; /** * Created by lsq on 2018/3/13. * 飲料基類 */ public abstract class Beverage extends AbstractBaseFood implements IFood{ public void printMessage(){ System.out.println("--"+this.kind+"飲料,\t單價:"+this.price+ ",\t數量:"+this.num+",\t合計:"+this.totalPrice()); } }
3.4.3創建具體的食物
每個具體食物實現類也很簡單,采用由一個數量作為參數的構造方法,類別和單價在每種食物中都已經指明。
1)麻辣雞腿漢堡——ChinaHamburg
麻辣雞腿漢堡實現類ChinaHamburg,繼承漢堡基類Hamburg。
package com.demo.factory.model.kfc; import com.demo.factory.model.Hamburg; /** * Created by lsq on 2018/3/13. * 中國風味的麻辣雞腿漢堡 */ public class ChinaHamburg extends Hamburg{ /** * 構造方法 * @param num */ public ChinaHamburg(int num) { this.kind = "麻辣"; this.price = 14.0f; this.num = num; } }
2)奧爾良烤雞翅——ChinaChickenWings
package com.demo.factory.model.kfc; import com.demo.factory.model.ChickenWings; /** * Created by lsq on 2018/3/13. * 雞翅實現類 */ public class ChinaChickenWings extends ChickenWings{ /** * 構造方法 * @param num */ public ChinaChickenWings(int num) { this.kind = "奧爾良"; this.price = 2.5f; this.num = num; } }
3)薯條——ChinaFrenchFries
package com.demo.factory.model.kfc; import com.demo.factory.model.FrenchFries; /** * Created by lsq on 2018/3/13. * 薯條實現類 */ public class ChinaFrenchFries extends FrenchFries{ public ChinaFrenchFries(int num) { this.kind = "普通"; this.price = 8.0f; this.num = num; } }
4)可樂——ChinaBeverage
package com.demo.factory.model.kfc; import com.demo.factory.model.Beverage; /** * Created by lsq on 2018/3/13. * 薯條實現類 */ public class ChinaBeverage extends Beverage{ public ChinaBeverage(int num) { this.kind = "可樂"; this.price = 7.0f; this.num = num; } }
3.4.4建立工廠
1)創建抽象肯德基工廠——IKfcFactory生產抽象食物
產品已經有了,下面建立生產產品的抽象工廠。
package com.demo.factory.itf; import com.demo.factory.model.Beverage; import com.demo.factory.model.ChickenWings; import com.demo.factory.model.FrenchFries; import com.demo.factory.model.Hamburg; /** * Created by lsq on 2018/3/13. * 肯德基抽象工廠 */ public interface IKfcFactory { //生產漢堡 public Hamburg createHamburg(int num); //生產薯條 public FrenchFries createFrenchFries(int num); //生產雞翅 public ChickenWings createChickenWings(int num); //生產飲料 public Beverage createBeverage(int num); }
注意:抽象工廠中創建各種食物,都是抽象食物。
2)創建具體肯德基工廠——ChinaKfcFactory生產具體食物
package com.demo.factory.itf; import com.demo.factory.model.Beverage; import com.demo.factory.model.ChickenWings; import com.demo.factory.model.FrenchFries; import com.demo.factory.model.Hamburg; import com.demo.factory.model.kfc.ChinaBeverage; import com.demo.factory.model.kfc.ChinaChickenWings; import com.demo.factory.model.kfc.ChinaFrenchFries; import com.demo.factory.model.kfc.ChinaHamburg; /** * Created by lsq on 2018/3/13. * 具體工廠 */ public class ChinaKfcFactory implements IKfcFactory{ //生產漢堡 public Hamburg createHamburg(int num) { return new ChinaHamburg(num); } //生產薯條 public FrenchFries createFrenchFries(int num) { return new ChinaFrenchFries(num); } //生產雞翅 public ChickenWings createChickenWings(int num) { return new ChinaChickenWings(num); } //生產飲料 public Beverage createBeverage(int num) { return new ChinaBeverage(num); } }
3.4.5創建客戶類
客戶類中含有一個抽象工廠IKfcFactory類型的實例變量kfcFactory,客戶類Customer通過構造方法將肯德基店實例傳入,客戶需要食物時,就向肯德基店(工廠)請求,客戶不生產食物(不使用new生成對象)。
package com.demo.factory.custom; import com.demo.factory.itf.IKfcFactory; import com.demo.factory.model.Beverage; import com.demo.factory.model.ChickenWings; import com.demo.factory.model.FrenchFries; import com.demo.factory.model.Hamburg; /** * Created by lsq on 2018/3/13. * 客戶類 */ public class Customer { //抽象工廠 private IKfcFactory kfcFactory; //構造方法將抽象工廠作為參數傳入 public Customer(IKfcFactory kfcFactory){ this.kfcFactory = kfcFactory; } /** * 訂購食物 */ //訂購麻辣雞腿漢堡 public float orderHamburg(int num){ //獲得麻辣雞腿漢堡 Hamburg hamburg = kfcFactory.createHamburg(num); //輸出訂購信息 hamburg.printMessage(); //返回總價 return hamburg.totalPrice(); } //訂購奧爾良烤雞翅 public float orderChickenWings(int num){ //獲得奧爾良烤雞翅 ChickenWings chickenWings = kfcFactory.createChickenWings(num); //輸出訂購信息 chickenWings.printMessage(); //返回總價 return chickenWings.totalPrice(); } //訂購薯條 public float orderFrenchFries(int num){ //獲得薯條 FrenchFries frenchFries = kfcFactory.createFrenchFries(num); //輸出訂購信息 frenchFries.printMessage(); //返回總價 return frenchFries.totalPrice(); } //訂購可樂 public float orderBeverage(int num){ //獲得可樂 Beverage beverage = kfcFactory.createBeverage(num); //輸出訂購信息 beverage.printMessage(); //返回總價 return beverage.totalPrice(); } }
3.4.6客戶到店點餐
import com.demo.factory.custom.Customer; import com.demo.factory.itf.ChinaKfcFactory; import com.demo.factory.itf.IKfcFactory; /** * Created by lsq on 2018/3/13. * */ public class MainApp { public static void main(String[] args) { /** * 定義一個肯德基(IKfcFactory類型) */ IKfcFactory kfcFactory = new ChinaKfcFactory(); /** * 創建客戶 */ Customer customer = new Customer(kfcFactory); /** * 客戶開始點餐 */ //一個麻辣雞腿漢堡 float hamburgMoney = customer.orderHamburg(1); //四個奧爾良烤雞翅 float chickenWingsMoney = customer.orderChickenWings(4); //一包薯條 float frenchFriesMoney = customer.orderFrenchFries(1); //兩杯可樂 float beverageMoney = customer.orderBeverage(2); System.out.println("總計:"+(hamburgMoney+chickenWingsMoney+frenchFriesMoney+beverageMoney)); } }
運行結果:
3.6使用場合
1)創建產品家族,相關產品集合在一起使用的時候;
2)想要提供一個產品類庫,並只想顯示其接口而不是實現時;
3)通過組合的方式使用工廠時。
抽象工廠模式提供了一個接口,用於創建相關或者依賴對象的家族,而不需要指定具體實現類。抽象工廠模式是指當有多個抽象角色時使用的一種工廠模式。抽象工廠模式可以向客戶端提供一個接口,使用客戶端在不必指定具體產品的情況下,創建多個產品族中的產品對象。當有多個抽象產品角色時,工廠方法模式已經不能滿足要求。
在抽象工廠模式中,定義了一個抽象接口,如本例中的IKfcFactory,之后具體工廠又通過實現抽象工廠的各種接口方法創建實例對象,如本例中的ChinaKfcFactory,這不正是之前講的工廠方法模式嗎?的確,在抽象工廠模式中使用了工廠方法模式的實現方式。
3.7抽象工廠模式和工廠方法模式的區別
1)工廠方法模式通過繼承的方式實現應用程序的解耦,而抽象工廠模式則通過對象組合的方式實現應用程序的解耦;
2)工廠方法模式用來創建一個抽象產品,具體工廠實現工廠方法來創建具體產品,而抽象工廠模式用來創建一個產品家族的抽象類型。