【设计模式活用】之一个榨汁机应用场景示例

本文通过榨汁机示例,深入浅出地讲解了Java中模板方法模式、策略模式和工厂模式的应用。展示了如何根据不同水果调整榨汁比例,实现灵活的榨汁策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

现在有这样一个需求,通过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}

整体类图关系
在这里插入图片描述

下面的是我的公众号二维码图片,欢迎关注。
秋夜无霜

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值