03-肯德基點餐:抽象工廠模式


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)工廠方法模式用來創建一個抽象產品,具體工廠實現工廠方法來創建具體產品,而抽象工廠模式用來創建一個產品家族的抽象類型。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM