【設計模式活用】之一個榨汁機應用場景示例

現在有這樣一個需求,通過java實現一個榨汁機示例。榨汁機呢,現在支持水果(比如蘋果,香蕉),不同水果出汁比例不一樣(比如,1kg蘋果智能出0.3kg汁)。對於用戶(或者講客戶端)來講,他只關注只要能榨汁即可(不需要關注具體怎麼榨汁的,你搗鼓蘋果,它榨出蘋果汁,你搗鼓香蕉,它榨出香蕉汁)。對於用戶來講,榨汁機有普通版,升級版,豪華版,不同檔次榨汁機功能菜單肯定也不一樣。
代碼地址:源碼地址

1、原料定義

我們分析需求,發現既然是榨汁機,那麼肯定是榨汁機能夠榨的東西,你給它塊石頭,也炸不出汁。

該抽象類AbstractFruit派生兩個子類,Apple和Banana,對於派生的其他水果,只需繼承這個抽象類即可。AbstractFruit包括name、color、weight屬性

抽象水果類:AbstractFruit

/**
 * @description: 抽象水果類
 * @Date : 2018/9/20 下午1:24
 * @Author : 石鼕鼕-Seig Heil
 */
public abstract class AbstractFruit {
    /**
     * 名稱
     */
    protected String name;
    /**
     * 顏色
     */
    protected String color;
    /**
     * 重量
     */
    protected double weight;

    public AbstractFruit() {
    }

    public AbstractFruit(String name, String color, double weight) {
        this.name = name;
        this.color = color;
        this.weight = weight;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public double getWeight() {
        return weight;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "AbstractFruit{" +
                "name='" + name + '\'' +
                ", color='" + color + '\'' +
                ", weight=" + weight +
                '}';
    }
}

具體水果類:Apple

/**
 * @description: 蘋果類
 * @Date : 2018/9/20 下午1:27
 * @Author : 石鼕鼕-Seig Heil
 */
public class Apple extends AbstractFruit {

    public Apple() {
    }

    public Apple(String name, String color, double weight) {
        super(name, color, weight);
    }

    @Override
    public String toString() {
        return super.toString();
    }
}

具體水果類:Banana

/**
 * @description: 香蕉
 * @Date : 2018/9/20 下午1:27
 * @Author : 石鼕鼕-Seig Heil
 */
public class Banana extends AbstractFruit {

    public Banana() {
    }

    public Banana(String name, String color, double weight) {
        super(name, color, weight);
    }

    @Override
    public String toString() {
        return super.toString();
    }
}

2、機器定義

既然是榨汁機,肯定是個機器,機器呢,它支持榨汁,你給他原料(水果)它就可以榨汁,輸出是啥呢?肯定是果汁,所以這個機器可以榨汁,給它原料,輸出果汁。

榨汁接口:Juicing

/**
 * @description: 榨汁接口
 * @Date : 2018/9/20 下午1:20
 * @Author : 石鼕鼕-Seig Heil
 */
public interface Juicing {
    /**
     * 榨汁動作
     */
    void press();
}

果汁類:Juice

/**
 * @description: 果汁
 * @Date : 2018/9/20 下午1:54
 * @Author : 石鼕鼕-Seig Heil
 */
public class Juice {
    /**
     * 名稱
     */
    private String name;
    /**
     * 重量
     */
    private double weight;

    public Juice(String name, double weight) {
        this.name = name;
        this.weight = weight;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getWeight() {
        return weight;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "Juice{" +
                "name='" + name + '\'' +
                ", weight=" + weight +
                '}';
    }
}

抽象榨汁機:AbstractJuicer

/**
 * @description: 抽象榨汁機
 * @Date : 2018/9/20 下午1:19
 * @Author : 石鼕鼕-Seig Heil
 */
public abstract class AbstractJuicer implements Juicing {
    /**
     * 水果
     */
    protected AbstractFruit fruit;
    /**
     * 果汁
     */
    protected Juice juice;
    /**
     * 名稱
     */
    private String materialName;
    /**
     *
     */
    private double materialWeight;

    public AbstractJuicer(AbstractFruit fruit) {
        this.fruit = fruit;
    }

    /**
     * 初始化
     */
    protected void init(){
        materialWeight = fruit.getWeight();
        materialName = fruit.getName();
    }

    /**
     * 外部調用
     */
    public final void execute(){
        init();
        press();
    }

    @Override
    public void press() {
        AbstractJuiceStrategy strategy = StrategyFactory.create(materialName);
        ScaleContext scaleContext = new ScaleContext(materialWeight);
        strategy.setContext(scaleContext);
        strategy.execute();
        juice = new Juice(materialName,scaleContext.getOutWeight());
    }

    /**
     * 獲取果汁
     * @return
     */
    public Juice getJuice() {
        return juice;
    }
}

我們從上述代碼中可以分析,上面使用了模板方法模式。對外暴露的方法聲明未final類型,是不允許被重寫的,內部先初始化原料,然後調用榨汁接口,而實現的榨汁接口,內部先初始化配比上線文,通過調用配比榨汁策略,給出對應的原料名稱返回配比結果,根據配比結果,最後輸出果汁。

通用榨汁機類:GeneralJuicer

/**
 * @description: 通用榨汁機
 * @Date : 2018/9/20 下午7:40
 * @Author : 石鼕鼕-Seig Heil
 */
public class GeneralJuicer extends AbstractJuicer {
    public GeneralJuicer(AbstractFruit fruit) {
        super(fruit);
    }
}

3、榨汁比例策略

不同水果榨汁比例肯定不一樣,既然達到系統預置,肯定榨汁機根據菜單預置功能從生產就確定的了。

比例配置類:ScaleConfig

這裏我們通過一個枚舉定義,指定不同水果配置比例,後期擴展時,只需要添加枚舉成員即可。

/**
 * @description: 比例配置
 * @Date : 2018/9/20 下午3:42
 * @Author : 石鼕鼕-Seig Heil
 */
public enum ScaleConfig {
    APPLE("蘋果",0.6),
    BANANA("香蕉",0.3)
    ;

    ScaleConfig(String meterial, double scale) {
        this.meterial = meterial;
        this.scale = scale;
    }

    /**
     * 原料
     */
    private String meterial;
    /**
     * 原料與榨汁輸出比例
     */
    private double scale;

    public String getMeterial() {
        return meterial;
    }

    public double getScale() {
        return scale;
    }

    public static double getScale(String meterial){
        for(ScaleConfig config : ScaleConfig.values()){
            if(config.getMeterial().equals(meterial)){
                return config.getScale();
            }
        }
        return 0d;
    }
}

榨汁策略上線文:ScaleContext

上下文類,包含原料重量和輸出重量,即榨汁配比。

/**
 * @description: 榨汁策略上線文
 * @Date : 2018/9/20 下午3:40
 * @Author : 石鼕鼕-Seig Heil
 */
public class ScaleContext {
    /**
     * 原料重量
     */
    protected double materialWeight;
    /**
     * 輸出
     */
    protected double outWeight;

    public ScaleContext(double materialWeight) {
        this.materialWeight = materialWeight;
    }

    public double getMaterialWeight() {
        return materialWeight;
    }

    public void setMaterialWeight(double materialWeight) {
        this.materialWeight = materialWeight;
    }

    public double getOutWeight() {
        return outWeight;
    }

    public void setOutWeight(double outWeight) {
        this.outWeight = outWeight;
    }
}

抽象榨汁輸出策略類:AbstractJuiceStrategy

這裏我們抽象出一個策略類,不同水果榨汁比例不一樣,只需派生該抽象類即可。
抽象類包含定義了一個抽象方法getScale,同時execute方法,把配比方法骨架封裝起來,使用模板方法模式。

/**
 * @description: 抽象榨汁輸出策略類
 * @Date : 2018/9/20 下午3:48
 * @Author : 石鼕鼕-Seig Heil
 */
public abstract class AbstractJuiceStrategy {
    /**
     * 榨汁上下文對象
     */
    protected ScaleContext context;

    /**
     * 原料名稱
     */
    protected String material;
    /**
     * 原料重量
     */
    protected double materialWeight;
    /**
     * 輸出
     */
    protected double outWeight;
    /**
     * 獲取比例
     * @return
     */
    abstract double getScale();

    public AbstractJuiceStrategy() {
    }

    /**
     * 構造函數
     * 通過構造函數注入context
     * @param context
     */
    public AbstractJuiceStrategy(String material,ScaleContext context) {
        this.material = material;
        this.context = context;
    }

    /**
     * 初始化
     */
    protected void init(){
        this.materialWeight = context.getMaterialWeight();
        System.out.println(MessageFormat.format("原料輸入中,當前原料={0},重量={1}",material,materialWeight));
    }

    /**
     * 配比加工
     */
    protected void match(){
        this.outWeight = this.materialWeight * getScale();
        System.out.println("輸出重量"+this.outWeight);
    }

    /**
     * 後置處理
     */
    protected void after(){
        this.context.setOutWeight(outWeight);
    }

    /**
     * 外部調用
     */
    public final void execute(){
        init();
        match();
        after();
    }

    public void setContext(ScaleContext context) {
        this.context = context;
    }
}

蘋果原料果汁榨汁配比策略類:AppleJuiceStrategy

/**
 * @description: 蘋果原料果汁榨汁配比策略類
 * @Date : 2018/9/20 下午3:58
 * @Author : 石鼕鼕-Seig Heil
 */
public class AppleJuiceStrategy extends AbstractJuiceStrategy {

    @Override
    double getScale() {
        return ScaleConfig.getScale(super.material);
    }

    public AppleJuiceStrategy() {
    }

    public AppleJuiceStrategy(String material, ScaleContext context) {
        super(material, context);
    }

    public AppleJuiceStrategy(ScaleContext context) {
        super("蘋果",context);
    }
}

香蕉原料果汁榨汁配比策略類:BananaJuiceStrategy

/**
 * @description: 香蕉原料果汁榨汁配比策略類
 * @Date : 2018/9/20 下午3:58
 * @Author : 石鼕鼕-Seig Heil
 */
public class BananaJuiceStrategy extends AbstractJuiceStrategy {

    @Override
    double getScale() {
        return ScaleConfig.getScale(super.material);
    }

    public BananaJuiceStrategy() {
    }

    public BananaJuiceStrategy(String material, ScaleContext context) {
        super(material, context);
    }

    public BananaJuiceStrategy(ScaleContext context) {
        super("香蕉",context);
    }
}

策略工廠類:StrategyFactory

對於調用發,我們通過簡單工廠實現策略類的實例創建,對於輸入原料,不關心具體如何配比,只需要傳入原料名稱即可,內部根據名稱獲取不同策略類。

/**
 * @description: 策略工廠類
 * @Date : 2018/9/20 下午4:04
 * @Author : 石鼕鼕-Seig Heil
 */
public enum StrategyFactory {
    APPLE("蘋果"),
    BANANA("香蕉"),
    ;

    StrategyFactory(String material) {
        this.material = material;
    }

    /**
     * 原料
     */
    private String material;

    public String getMaterial() {
        return material;
    }

    /**
     * 策略生產
     * @param material
     * @return
     */
    public static AbstractJuiceStrategy create(String material){
        switch (material){
            case "蘋果":
                return new AppleJuiceStrategy("蘋果",new ScaleContext(0d));
            case "香蕉":
                return new BananaJuiceStrategy("香蕉",new ScaleContext(0d));
        }
        return null;
    }
}

4、榨汁機集成器

爲什麼這麼定義這個術語呢,既然是榨汁機,肯定是生產商在生產時,都已經把零部件集成安裝完畢,對於用戶來講,只需要暴露使用說明書。你不需要明白榨汁機具體怎麼榨汁,水果能榨汁多少,你只需要按照說明書上的要求,即可榨汁你放的水果。

執行器接口:Executor

/**
 * @description: 執行器接口
 * @Date : 2018/9/20 下午7:38
 * @Author : 石鼕鼕-Seig Heil
 */
public interface Executor {
    /**
     * 執行
     */
    void execute();
}

執行器上下文:WrapperContext

該上線文對象,包括幾個成員果汁機,原料,以及輸出果汁,所以它承載着是一個相當於中介的作用,對於果汁機,原料,以及輸出果汁無需調用彼此,只需要委託給上線文對象即可。

/**
 * @description: 程序上下文對象,承載果汁機和水果的包裝
 * @Date : 2018/9/20 下午1:31
 * @Author : 石鼕鼕-Seig Heil
 */
public class WrapperContext {
    /**
     * 果汁機
     */
    protected AbstractJuicer juicer;
    /**
     * 原料:水果
     */
    protected AbstractFruit fruit;
    /**
     * 果汁
     */
    protected Juice juice;

    public WrapperContext(AbstractJuicer juicer, AbstractFruit fruit) {
        this.juicer = juicer;
        this.fruit = fruit;
    }

    public AbstractJuicer getJuicer() {
        return juicer;
    }

    public void setJuicer(AbstractJuicer juicer) {
        this.juicer = juicer;
    }

    public Juice getJuice() {
        return juice;
    }

    public void setJuice(Juice juice) {
        this.juice = juice;
    }

    public AbstractFruit getFruit() {
        return fruit;
    }

    public void setFruit(AbstractFruit fruit) {
        this.fruit = fruit;
    }

    public WrapperContext(AbstractJuicer juicer, AbstractFruit fruit, Juice juice) {
        this.juicer = juicer;
        this.fruit = fruit;
        this.juice = juice;
    }
}

抽象包裝執行器:WrapperExecutor

果汁機,原料,以及輸出果汁委託給WrapperContext,那具體執行者,就委託給WrapperExecutor,內部承載着對象的調用。

/**
 * @description: 抽象包裝執行器
 * @Date : 2018/9/20 下午1:31
 * @Author : 石鼕鼕-Seig Heil
 */
public abstract class WrapperExecutor implements Executor{

    protected WrapperContext context;

    public WrapperExecutor(WrapperContext context) {
        this.context = context;
    }

    @Override
    public void execute() {
        AbstractJuicer juicer = context.getJuicer();
        juicer.execute();
        context.setJuice(juicer.getJuice());
    }
}

通用包裝執行器:GeneralExecutor

/**
 * @description: 通用包裝執行器
 * @Date : 2018/9/20 下午7:47
 * @Author : 石鼕鼕-Seig Heil
 */
public class GeneralExecutor extends WrapperExecutor {
    public GeneralExecutor(WrapperContext context) {
        super(context);
    }
}

5、測試

/**
 * @description:
 * @Date : 2018/9/20 下午7:39
 * @Author : 石鼕鼕-Seig Heil
 */
public class JuicerTest {

    @Test
    public void apple(){
        AbstractFruit apple = new Apple(StrategyFactory.APPLE.getMaterial(),"紅色",1000);
        GeneralJuicer juicer = new GeneralJuicer(apple);
        WrapperContext context = new WrapperContext(juicer,apple);
        GeneralExecutor executor = new GeneralExecutor(context);
        executor.execute();
        System.out.println(context.getJuice());
    }

    @Test
    public void banana(){
        AbstractFruit apple = new Banana(StrategyFactory.BANANA.getMaterial(),"紅色",1000);
        GeneralJuicer juicer = new GeneralJuicer(apple);
        WrapperContext context = new WrapperContext(juicer,apple);
        GeneralExecutor executor = new GeneralExecutor(context);
        executor.execute();
        System.out.println(context.getJuice());
    }
}

輸出結果

原料輸入中,當前原料=香蕉,重量=1,000
輸出重量300.0
Juice{name=‘香蕉’, weight=300.0}
原料輸入中,當前原料=蘋果,重量=1,000
輸出重量600.0
Juice{name=‘蘋果’, weight=600.0}

整體類圖關係
在這裏插入圖片描述

下面的是我的公衆號二維碼圖片,歡迎關注。
秋夜無霜

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章