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

〇、小故事

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

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

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

如果已经投了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^)/ ~ 「干货分享,每天更新」

相关推荐
摇滚侠15 小时前
Spring Boot 3零基础教程,IOC容器中组件的注册,笔记08
spring boot·笔记·后端
程序员小凯17 小时前
Spring Boot测试框架详解
java·spring boot·后端
你的人类朋友18 小时前
什么是断言?
前端·后端·安全
程序员小凯19 小时前
Spring Boot缓存机制详解
spring boot·后端·缓存
i学长的猫19 小时前
Ruby on Rails 从0 开始入门到进阶到高级 - 10分钟速通版
后端·ruby on rails·ruby
用户214118326360220 小时前
别再为 Claude 付费!Codex + 免费模型 + cc-switch,多场景 AI 编程全搞定
后端
茯苓gao20 小时前
Django网站开发记录(一)配置Mniconda,Python虚拟环境,配置Django
后端·python·django
Cherry Zack20 小时前
Django视图进阶:快捷函数、装饰器与请求响应
后端·python·django
爱读源码的大都督20 小时前
为什么有了HTTP,还需要gPRC?
java·后端·架构
码事漫谈20 小时前
致软件新手的第一个项目指南:阶段、文档与破局之道
后端