趣解设计模式之《小王的糖果售卖机》

〇、小故事

小王最近一直在寻找商机,他发现商场儿童乐园或者中小学校周围,会有很多小朋友喜欢吃糖果,那么他想设计一款糖果售卖机,让后将这些糖果售卖机布置到商场和学校旁边,这样就能获得源源不断的收益了。

想到这里,说干就干,他绘制出了一台糖果售卖机的操作状态关系流转图,请见下图所示:

如果我们以动作做为一个方法去处理,比如:投入钱币的动作,那么我们就需要按照如下方式去实现方法中的逻辑:

如果已经投了25分钱 】提示"已经投入钱币了,你不能再投入钱币了!";

如果之前没有投过钱 】提示"投入钱币成功!",并且当前的糖果机状态从未投币状态 变为已投币状态

如果糖果机里无糖果 】提示"糖果已经卖光了,你不能往里投入钱币了!"

如果糖果正在出货中 】提示"请等一等,糖果正在出货中。你不用在投入钱币了!"

针对以上逻辑,代码实现如下所示:

java 复制代码
final static int SOLD_OUT = 0; // 糖果售罄
final static int NO_QUARTER = 1; // 没有投入钱币
final static int HAS_QUARTER = 2; // 已经投入钱币
final static int SOLD = 3; // 正在出售糖果

public void insertQuarter() {
    if(state == HAS_QUARTER) {
        System.out.println("已经投入钱币了,你不能再投入钱币了");
    } else if(state == NO_QUARTER) {
        state = HAS_QUARTER;// 将糖果机的状态改为HAS_QUARTER(已投币)
        System.out.println("投入钱币成功");
    } if(state == SOLD_OUT) {
        System.out.println("糖果已经卖光了,你不能往里投入钱币了");
    } if(state == SOLD) {
        System.out.println("请等一等,糖果正在出货中。你不用在投入钱币了");
    }
}

那么其他的动作,比例:转动曲柄退回钱币操作发放糖果 等,也需要按照上面的写法去实现逻辑。显然,通过这么一大堆的if...else是不优雅的,而且当增加一个全新的状态的时候,所有的动作都需要兼容这个新的动作,那么,这个就是很明显的基于过程编程了,针对以上的问题,我们可以使用今天要介绍的设计模式来解决------状态模式

一、模式定义

状态模式State Pattern

允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

状态模式和策略模式的区别------意图是不同的

状态模式 】随着时间流逝,当前状态在状态对象集合中游走改变 ,以反映出context内部的状态。客户对其基本不了解。它是针对具有很多条件判断 的替代方案。

策略模式 】客户通常主动指定 context索要组合的策略对象是哪一个。它是继承之外的一种弹性替代方案。

二、模式类图

针对上面的介绍,我们来构造一下状态模式的类图,首先,创建状态接口 State.java,接口里面包含了"插入硬币"、"退出硬币"、"扭转曲柄"和"发放糖果"这四个操作,那么具体的动作实现逻辑是与不同状态实现类一一对应的,也就是说,不同状态实现类负责各自的四个动作方法的具体实现 。针对状态接口,我们创建4个实现类,分别是:糖果售卖状态类 SoldState.java糖果售空状态类 SoldOutState.java已经投放钱币状态类 HasQuarterState.java没有投放钱币状态类 NoQuarterState.java,那么当前处于哪个状态则由糖果售卖机类GumballMachine.java维护。具体类图请见下图所示:

三、代码实现

状态接口类State.java

java 复制代码
public interface State {
    void insertQuarter(); // 投入硬币操作   
    void ejectQuarter(); // 退出硬币操作
    void turnCrank(); // 扭转曲柄操作
    void dispense(); // 发放糖果操作
}

糖果售卖状态类SoldState.java

java 复制代码
public class SoldState implements State {

    private GumballMachine gumballMachine;

    public SoldState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }

    @Override
    public void insertQuarter() {
        System.out.println("糖果正在出货中,请稍等。无须再次投入钱币!");
    }

    @Override
    public void ejectQuarter() {
        System.out.println("糖果正在出货中,请稍等。不能退回钱币!");
    }

    @Override
    public void turnCrank() {
        System.out.println("糖果正在出货中,请稍等。不需要再次扭转曲柄!");
    }

    @Override
    public void dispense() {
        if (gumballMachine.getCount() > 0) {
            System.out.println("糖果正在出货中,请稍等!");
            gumballMachine.releaseBall();
            gumballMachine.setState(gumballMachine.getNoQuarterState()); // 状态流转
        } else {
            System.out.println("糖果库存不足,无法出货!");
            gumballMachine.setState(gumballMachine.getSoldOutState()); // 状态流转
        }
    }
}

糖果售空状态类SoldOutState.java

java 复制代码
public class SoldOutState implements State {

    private GumballMachine gumballMachine;

    public SoldOutState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }

    @Override
    public void insertQuarter() {
        System.out.println("糖果已经售罄。不能投入钱币");
    }

    @Override
    public void ejectQuarter() {
        System.out.println("退回钱币成功!");
    }

    @Override
    public void turnCrank() {
        System.out.println("糖果已经售罄。不能扭转曲柄!");
    }

    @Override
    public void dispense() {
        System.out.println("糖果已经售罄。糖果无法出售!");
    }
}

已经投放钱币状态类HasQuarterState.java

java 复制代码
public class HasQuarterState implements State {

    private GumballMachine gumballMachine;

    public HasQuarterState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }

    @Override
    public void insertQuarter() {
        System.out.println("您已经投入钱币!无须再次投入钱币!");
    }

    @Override
    public void ejectQuarter() {
        System.out.println("退款成功!");
        gumballMachine.setState(gumballMachine.getNoQuarterState()); // 状态流转
    }

    @Override
    public void turnCrank() {
        System.out.println("正在出货中,请稍等");
        gumballMachine.setState(gumballMachine.getSoldState()); // 状态流转
    }

    @Override
    public void dispense() {
        System.out.println("你还没有扭转曲柄,糖果不可以发放!");
    }
}

没有投放钱币状态类NoQuarterState.java

java 复制代码
public class NoQuarterState implements State {

    private GumballMachine gumballMachine;

    public NoQuarterState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }

    @Override
    public void insertQuarter() {
        System.out.println("投入钱币成功!");
        gumballMachine.setState(gumballMachine.getHasQuarterState()); // 状态流转
    }

    @Override
    public void ejectQuarter() {
        System.out.println("你还没有投入钱币,不能退回钱币!");
    }

    @Override
    public void turnCrank() {
        System.out.println("你还没有投入钱币,不能扭转曲柄!");
    }

    @Override
    public void dispense() {
        System.out.println("你还没有投入钱币,糖果不可以发放!");
    }
}

糖果售卖机类GumballMachine.java

java 复制代码
@Data
public class GumballMachine {
    private State noQuarterState;
    private State hasQuarterState;
    private State soldState;
    private State soldOutState;
    private State state = soldOutState; // 糖果机默认状态为售罄状态
    int count = 0; // 糖果库存量

    public GumballMachine(int numberGumballs) {
        noQuarterState = new NoQuarterState(this);
        hasQuarterState = new HasQuarterState(this);
        soldState = new SoldState(this);
        soldOutState = new SoldOutState(this);
        count = numberGumballs;
        if (numberGumballs > 0) {
            state = noQuarterState; // 如果采购了糖果球(numberGumballs>0),则糖果机的状态为未投币状态
        }
    }

    // 投入钱币
    public void insertQuarter() {
        state.insertQuarter();
    }

    // 退出钱币
    public void ejectQuarter() {
        state.ejectQuarter();
    }

    // 扭转曲柄
    public void turnCrank() {
        state.turnCrank();
        state.dispense();
    }

    // 减少库存
    public void releaseBall() {
        if (count > 0) {
            System.out.println("一个糖果球正在出库");
            --count;
        } else {
            System.out.println("库存不足,一个糖果球无法出库");
        }
    }

    // 设置状态
    void setState(State state) {
        this.state = state;
    }
}

状态模式测试类StateTest.java

java 复制代码
public class StateTest {
    public static void main(String[] args) {
        System.out.println("-----向糖果机中放入1枚糖果-----");
        GumballMachine machine = new GumballMachine(1);

        System.out.println("-----第一次购买糖果-----");
        machine.insertQuarter();
        machine.ejectQuarter();
        machine.turnCrank();

        System.out.println("-----第二次购买糖果-----");
        machine.insertQuarter();
        machine.turnCrank();

        System.out.println("-----第三次购买糖果-----");
        machine.insertQuarter();
        machine.turnCrank();
        machine.ejectQuarter();
    }
}

今天的文章内容就这些了:

写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享

更多技术干货,欢迎大家关注公众号"爪哇缪斯" ~ \(^o^)/ ~ 「干货分享,每天更新」

相关推荐
2401_8576363920 分钟前
计算机课程管理平台:Spring Boot与工程认证的结合
java·spring boot·后端
也无晴也无风雨1 小时前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
2401_857610035 小时前
多维视角下的知识管理:Spring Boot应用
java·spring boot·后端
代码小鑫5 小时前
A027-基于Spring Boot的农事管理系统
java·开发语言·数据库·spring boot·后端·毕业设计
颜淡慕潇6 小时前
【K8S问题系列 | 9】如何监控集群CPU使用率并设置告警?
后端·云原生·容器·kubernetes·问题解决
独泪了无痕6 小时前
WebStorm 如何调试 Vue 项目
后端·webstorm
怒放吧德德8 小时前
JUC从实战到源码:JMM总得认识一下吧
java·jvm·后端
代码小鑫8 小时前
A025-基于SpringBoot的售楼管理系统的设计与实现
java·开发语言·spring boot·后端·毕业设计
前端SkyRain8 小时前
后端SpringBoot学习项目-项目基础搭建
spring boot·后端·学习
梦想画家8 小时前
理解Rust 生命周期、所有权和借用机制
开发语言·后端·rust