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


    〇、小故事

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

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

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

    如果已经投了25分钱】提示“已经投入钱币了,你不能再投入钱币了!”;
    如果之前没有投过钱】提示“投入钱币成功!”,并且当前的糖果机状态从未投币状态变为已投币状态
    如果糖果机里无糖果】提示“糖果已经卖光了,你不能往里投入钱币了!
    如果糖果正在出货中】提示“请等一等,糖果正在出货中。你不用在投入钱币了!

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

    1. final static int SOLD_OUT = 0// 糖果售罄
    2. final static int NO_QUARTER = 1// 没有投入钱币
    3. final static int HAS_QUARTER = 2// 已经投入钱币
    4. final static int SOLD = 3// 正在出售糖果
    5. public void insertQuarter() {
    6.     if(state == HAS_QUARTER) {
    7.         System.out.println("已经投入钱币了,你不能再投入钱币了");
    8.     } else if(state == NO_QUARTER) {
    9.         state = HAS_QUARTER;// 将糖果机的状态改为HAS_QUARTER(已投币)
    10.         System.out.println("投入钱币成功");
    11.     } if(state == SOLD_OUT) {
    12.         System.out.println("糖果已经卖光了,你不能往里投入钱币了");
    13.     } if(state == SOLD) {
    14.         System.out.println("请等一等,糖果正在出货中。你不用在投入钱币了");
    15.     }
    16. }

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

    一、模式定义

    状态模式State Pattern

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

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

    状态模式】随着时间流逝,当前状态在状态对象集合中游走改变,以反映出context内部的状态。客户对其基本不了解。它是针对具有很多条件判断的替代方案。
    策略模式】客户通常主动指定context索要组合的策略对象是哪一个。它是继承之外的一种弹性替代方案。

    二、模式类图

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

    三、代码实现

    状态接口类State.java

    1. public interface State {
    2.     void insertQuarter()// 投入硬币操作   
    3.     void ejectQuarter()// 退出硬币操作
    4.     void turnCrank()// 扭转曲柄操作
    5.     void dispense()// 发放糖果操作
    6. }

    糖果售卖状态类SoldState.java

    1. public class SoldState implements State {
    2.     private GumballMachine gumballMachine;
    3.     public SoldState(GumballMachine gumballMachine) {
    4.         this.gumballMachine = gumballMachine;
    5.     }
    6.     @Override
    7.     public void insertQuarter() {
    8.         System.out.println("糖果正在出货中,请稍等。无须再次投入钱币!");
    9.     }
    10.     @Override
    11.     public void ejectQuarter() {
    12.         System.out.println("糖果正在出货中,请稍等。不能退回钱币!");
    13.     }
    14.     @Override
    15.     public void turnCrank() {
    16.         System.out.println("糖果正在出货中,请稍等。不需要再次扭转曲柄!");
    17.     }
    18.     @Override
    19.     public void dispense() {
    20.         if (gumballMachine.getCount() > 0) {
    21.             System.out.println("糖果正在出货中,请稍等!");
    22.             gumballMachine.releaseBall();
    23.             gumballMachine.setState(gumballMachine.getNoQuarterState()); // 状态流转
    24.         } else {
    25.             System.out.println("糖果库存不足,无法出货!");
    26.             gumballMachine.setState(gumballMachine.getSoldOutState()); // 状态流转
    27.         }
    28.     }
    29. }

    糖果售空状态类SoldOutState.java

    1. public class SoldOutState implements State {
    2.     private GumballMachine gumballMachine;
    3.     public SoldOutState(GumballMachine gumballMachine) {
    4.         this.gumballMachine = gumballMachine;
    5.     }
    6.     @Override
    7.     public void insertQuarter() {
    8.         System.out.println("糖果已经售罄。不能投入钱币");
    9.     }
    10.     @Override
    11.     public void ejectQuarter() {
    12.         System.out.println("退回钱币成功!");
    13.     }
    14.     @Override
    15.     public void turnCrank() {
    16.         System.out.println("糖果已经售罄。不能扭转曲柄!");
    17.     }
    18.     @Override
    19.     public void dispense() {
    20.         System.out.println("糖果已经售罄。糖果无法出售!");
    21.     }
    22. }

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

    1. public class HasQuarterState implements State {
    2.     private GumballMachine gumballMachine;
    3.     public HasQuarterState(GumballMachine gumballMachine) {
    4.         this.gumballMachine = gumballMachine;
    5.     }
    6.     @Override
    7.     public void insertQuarter() {
    8.         System.out.println("您已经投入钱币!无须再次投入钱币!");
    9.     }
    10.     @Override
    11.     public void ejectQuarter() {
    12.         System.out.println("退款成功!");
    13.         gumballMachine.setState(gumballMachine.getNoQuarterState()); // 状态流转
    14.     }
    15.     @Override
    16.     public void turnCrank() {
    17.         System.out.println("正在出货中,请稍等");
    18.         gumballMachine.setState(gumballMachine.getSoldState()); // 状态流转
    19.     }
    20.     @Override
    21.     public void dispense() {
    22.         System.out.println("你还没有扭转曲柄,糖果不可以发放!");
    23.     }
    24. }

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

    1. public class NoQuarterState implements State {
    2.     private GumballMachine gumballMachine;
    3.     public NoQuarterState(GumballMachine gumballMachine) {
    4.         this.gumballMachine = gumballMachine;
    5.     }
    6.     @Override
    7.     public void insertQuarter() {
    8.         System.out.println("投入钱币成功!");
    9.         gumballMachine.setState(gumballMachine.getHasQuarterState()); // 状态流转
    10.     }
    11.     @Override
    12.     public void ejectQuarter() {
    13.         System.out.println("你还没有投入钱币,不能退回钱币!");
    14.     }
    15.     @Override
    16.     public void turnCrank() {
    17.         System.out.println("你还没有投入钱币,不能扭转曲柄!");
    18.     }
    19.     @Override
    20.     public void dispense() {
    21.         System.out.println("你还没有投入钱币,糖果不可以发放!");
    22.     }
    23. }

    糖果售卖机类GumballMachine.java

    1. @Data
    2. public class GumballMachine {
    3.     private State noQuarterState;
    4.     private State hasQuarterState;
    5.     private State soldState;
    6.     private State soldOutState;
    7.     private State state = soldOutState; // 糖果机默认状态为售罄状态
    8.     int count = 0// 糖果库存量
    9.     public GumballMachine(int numberGumballs) {
    10.         noQuarterState = new NoQuarterState(this);
    11.         hasQuarterState = new HasQuarterState(this);
    12.         soldState = new SoldState(this);
    13.         soldOutState = new SoldOutState(this);
    14.         count = numberGumballs;
    15.         if (numberGumballs > 0) {
    16.             state = noQuarterState; // 如果采购了糖果球(numberGumballs>0),则糖果机的状态为未投币状态
    17.         }
    18.     }
    19.     // 投入钱币
    20.     public void insertQuarter() {
    21.         state.insertQuarter();
    22.     }
    23.     // 退出钱币
    24.     public void ejectQuarter() {
    25.         state.ejectQuarter();
    26.     }
    27.     // 扭转曲柄
    28.     public void turnCrank() {
    29.         state.turnCrank();
    30.         state.dispense();
    31.     }
    32.     // 减少库存
    33.     public void releaseBall() {
    34.         if (count > 0) {
    35.             System.out.println("一个糖果球正在出库");
    36.             --count;
    37.         } else {
    38.             System.out.println("库存不足,一个糖果球无法出库");
    39.         }
    40.     }
    41.     // 设置状态
    42.     void setState(State state) {
    43.         this.state = state;
    44.     }
    45. }

    状态模式测试类StateTest.java

    1. public class StateTest {
    2.     public static void main(String[] args) {
    3.         System.out.println("-----向糖果机中放入1枚糖果-----");
    4.         GumballMachine machine = new GumballMachine(1);
    5.         System.out.println("-----第一次购买糖果-----");
    6.         machine.insertQuarter();
    7.         machine.ejectQuarter();
    8.         machine.turnCrank();
    9.         System.out.println("-----第二次购买糖果-----");
    10.         machine.insertQuarter();
    11.         machine.turnCrank();
    12.         System.out.println("-----第三次购买糖果-----");
    13.         machine.insertQuarter();
    14.         machine.turnCrank();
    15.         machine.ejectQuarter();
    16.     }
    17. }

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

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

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

  • 相关阅读:
    【MySQL从入门到精通】【高级篇】(二十八)子查询优化,排序优化,GROUP BY优化和分页查询优化
    OpenJDK 19 的 5 个最关键和最具创新性的补充
    Java中SnowFlake 雪花算法生成全局唯一id中的问题,时间不连续全为偶数解决
    【华为OD机试真题 python】 We Are A Team【2022 Q4 | 100分】
    Unity引擎开发-无人机模拟飞行实现
    工作电压范围宽的国产音频限幅器D2761用于蓝牙音箱,输出噪声最大仅-90dBV
    【Git】.ignore文件修改后如何更新,删除已提交文件等问题
    基于Java的宠物商城管理系统设计与实现(源码+lw+部署文档+讲解等)
    【docker】基于dockerfile编写LNMP
    Linux-基础知识(黑马学习笔记)
  • 原文地址:https://blog.csdn.net/qq_26470817/article/details/133219008