当前位置: 首页 > news >正文

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

〇、小故事

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

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

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

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

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

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

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

糖果售卖状态类SoldState.java

public class SoldState implements State {private GumballMachine gumballMachine;public SoldState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("糖果正在出货中,请稍等。无须再次投入钱币!");}@Overridepublic void ejectQuarter() {System.out.println("糖果正在出货中,请稍等。不能退回钱币!");}@Overridepublic void turnCrank() {System.out.println("糖果正在出货中,请稍等。不需要再次扭转曲柄!");}@Overridepublic 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

public class SoldOutState implements State {private GumballMachine gumballMachine;public SoldOutState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("糖果已经售罄。不能投入钱币");}@Overridepublic void ejectQuarter() {System.out.println("退回钱币成功!");}@Overridepublic void turnCrank() {System.out.println("糖果已经售罄。不能扭转曲柄!");}@Overridepublic void dispense() {System.out.println("糖果已经售罄。糖果无法出售!");}
}

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

public class HasQuarterState implements State {private GumballMachine gumballMachine;public HasQuarterState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("您已经投入钱币!无须再次投入钱币!");}@Overridepublic void ejectQuarter() {System.out.println("退款成功!");gumballMachine.setState(gumballMachine.getNoQuarterState()); // 状态流转}@Overridepublic void turnCrank() {System.out.println("正在出货中,请稍等");gumballMachine.setState(gumballMachine.getSoldState()); // 状态流转}@Overridepublic void dispense() {System.out.println("你还没有扭转曲柄,糖果不可以发放!");}
}

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

public class NoQuarterState implements State {private GumballMachine gumballMachine;public NoQuarterState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("投入钱币成功!");gumballMachine.setState(gumballMachine.getHasQuarterState()); // 状态流转}@Overridepublic void ejectQuarter() {System.out.println("你还没有投入钱币,不能退回钱币!");}@Overridepublic void turnCrank() {System.out.println("你还没有投入钱币,不能扭转曲柄!");}@Overridepublic void dispense() {System.out.println("你还没有投入钱币,糖果不可以发放!");}
}

糖果售卖机类GumballMachine.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

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^)/ ~ 「干货分享,每天更新」

相关文章:

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

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

Redis 哨兵模式模式搭建教程

一、介绍 本文实战搭建一主两从三哨兵,通过使用哨兵模式,可以有效避免某台服务器的 Redis 挂掉出现的不可用问题,保障系统的高可用。 本文通过虚拟机搭建的三台 Centos7 服务器进行测试,使用的 Redis 版本为 6.25。 二、准备环…...

41. Linux系统配置FTP服务器并在QT中使用QFtp实现文件上传

1. 说明 这篇博客主要记录一些在Linux系统中搭建FTP服务器时踩过的一些坑,以及在使用QFtp上传文件时需要注意的问题。 2. FTP环境搭建 在linux系统中,需要安装vsftpd,可以在终端中输入下面的命令进行安装: sudo apt-get install vsftpd使用上述命令安装后,系统中会有一…...

【新版】系统架构设计师 - 案例分析 - 架构设计<架构风格和质量属性>

个人总结,仅供参考,欢迎加好友一起讨论 文章目录 架构 - 案例分析 - 架构设计<架构风格和质量属性>例题1例题2例题3例题4例题5例题6 架构 - 案例分析 - 架构设计<架构风格和质量属性> 例题1 某软件公司为…...

C++ - 红黑树 介绍 和 实现

前言 前面 学习了 AVL树,AVL树虽然在 查找方面始终拥有 O(log N )的极高效率,但是,AVL 树在插入 ,删除等等 修改的操作当中非常的麻烦,尤其是 删除操作,在实现当中细节非常多,在实现上非常难掌控…...

【蓝桥杯选拔赛真题62】Scratch判断小球 少儿编程scratch图形化编程 蓝桥杯选拔赛真题解析

目录 scratch判断小球 一、题目要求 编程实现 二、案例分析 1、角色分析...

Spring面试题15:Spring支持几种bean的作用域?singleton、prototype、request的区别是什么?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:Spring支持几种bean的作用域? Spring支持以下几种Bean的作用域: Singleton(单例):这是Spring默认的作用域。使用@Scope(“singleton”)注解或…...

Spring Boot中Tomcat服务器参数解析及高并发控制

Spring Boot中Tomcat服务器参数解析及高并发控制 Spring Boot 集成了多种服务器,默认使用了Tomcat 服务器。在高并发情况下,合理地配置 Tomcat 服务器参数对于控制请求量和提高系统的稳定性至关重要。本文将解释 Spring Boot 中涉及 Tomcat 服务器的一些…...

Python 运行代码

一、Python运行代码 可以使用三种方式运行Python,如下: 1、交互式 通过命令行窗口进入 Python 并开始在交互式解释器中开始编写 Python 代码 2、命令行脚本 可以把代码放到文件中,通过python 文件名.py命令执行代码,如下&#xff…...

【ROS入门】使用 ROS 话题(Topic)机制实现消息发布与订阅及launch文件的封装

文章结构 任务要求话题模型实现步骤创建工作空间并初始化创建功能包并添加依赖创建发布者代码(C)创建订阅方代码(C)配置CMakeLists.txt执行启动roscore编译启动发布和订阅节点 launch封装执行 任务要求 使用 ROS 话题(Topic)机制…...

【企业级SpringBoot单体项目模板 】——Mybatis-plus自动代码生成

😜作 者:是江迪呀✒️本文关键词:SpringBoot项目模版、企业级、模版☀️每日 一言:我们之所以这样认为,是因为他们这样说。他们之所以那样说,是因为他们想让我们那样认为。所以实践才是检验真理…...

怒刷LeetCode的第14天(Java版)

目录 第一题 题目来源 题目内容 解决方法 方法一:动态规划 方法二:栈 方法三:双指针 第二题 题目来源 题目内容 解决方法 方法一:二分查找 方法二:线性扫描 方法三:递归 第三题 题目来源 …...

c语言 static

1、静态局部变量在程序加载时初始化,静态局部变量的初始值写入到了data段: 如下代码test_symbol.c int f() {static int x 0;return x; }int g() {static int x 9;return x; }使用命令gcc -c test_symbol.c -o test_symbol 编译 使用命令 readelf -a …...

java基础3

输入一个班学生的成绩,先显示所有及格的成绩,再显示所有不及格的成绩,最后显示及格人数和不及格人数 import java.util.Scanner; public class Hello{public static void main(String [] args) {int SIZE5;double grade[] new double[SIZE]…...

LeetCode 1194.锦标赛优胜者

数据准备 Create table If Not Exists Players (player_id int, group_id int); Create table If Not Exists Matches (match_id int, first_player int, second_player int, first_score int, second_score int); Truncate table Players; insert into Players (player_id, g…...

多旋翼无人机组合导航系统-多源信息融合算法(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

如何用ArkUI实现一个加入购物车效果?

关键词:ArkUI的动效能力,动效开发,ArkUI动画 我们在购买商品时,往往习惯将商品先加入购物车,然后在购物车里确认后再下订单,这是一个典型的访问者模式。对于这个高频场景,增添一些动效可以增加a…...

ChatGLM GPT原理介绍

图解GPT 除了BERT以外,另一个预训练模型GPT也给NLP领域带来了不少轰动,本节也对GPT做一个详细的讲解。 OpenAI提出的GPT-2模型(https://openai.com/blog/better-language-models/) 能够写出连贯并且高质量的文章,比之前语言模型效果好很多。GPT-2是基于Transformer搭建的,相…...

2015年蓝桥杯省赛C/C++ A组 灾后重建题解(100分)

10. 灾后重建 Pear市一共有N&#xff08;<50000&#xff09;个居民点&#xff0c;居民点之间有M&#xff08;<200000&#xff09;条双向道路相连。这些居民点两两之间都可以通过双向道路到达。这种情况一直持续到最近&#xff0c;一次严重的地震毁坏了全部M条道路。 震后…...

Elasticsearch(四)深分页Scroll

一、前言 1.1、scroll与fromsize区别 ES对于fromsize的个数是有限制的&#xff0c;二者之和不能超过1w。当所请求的数据总量大于1w时&#xff0c;可用scroll来代替fromsize。 fromsize在ES查询数据的方式步骤如下&#xff1a; 1、先将用户指定的关键字进行分词&#xff1b;…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...

短视频矩阵系统文案创作功能开发实践,定制化开发

在短视频行业迅猛发展的当下&#xff0c;企业和个人创作者为了扩大影响力、提升传播效果&#xff0c;纷纷采用短视频矩阵运营策略&#xff0c;同时管理多个平台、多个账号的内容发布。然而&#xff0c;频繁的文案创作需求让运营者疲于应对&#xff0c;如何高效产出高质量文案成…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

比较数据迁移后MySQL数据库和OceanBase数据仓库中的表

设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

智能职业发展系统:AI驱动的职业规划平台技术解析

智能职业发展系统&#xff1a;AI驱动的职业规划平台技术解析 引言&#xff1a;数字时代的职业革命 在当今瞬息万变的就业市场中&#xff0c;传统的职业规划方法已无法满足个人和企业的需求。据统计&#xff0c;全球每年有超过2亿人面临职业转型困境&#xff0c;而企业也因此遭…...

多元隐函数 偏导公式

我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式&#xff0c;给定一个隐函数关系&#xff1a; F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 &#x1f9e0; 目标&#xff1a; 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z​、 …...