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

设计模式之十:状态模式

状态模式通过改变对象内部的状态来帮助对象控制自己的行为。

这是一张状态图,其中每个圆圈都是一个状态。

最简单,第一反应的实现就是使用一个变量来控制状态值,并在方法内书写条件代码来处理不同情况。

package headfirst.designpatterns.state.gumball;public class GumballMachine {final static int SOLD_OUT = 0;final static int NO_QUARTER = 1;final static int HAS_QUARTER = 2;final static int SOLD = 3;int state = SOLD_OUT;int count = 0;public GumballMachine(int count) {this.count = count;if (count > 0) {state = NO_QUARTER;}}public void insertQuarter() {if (state == HAS_QUARTER) {System.out.println("You can't insert another quarter");} else if (state == NO_QUARTER) {state = HAS_QUARTER;System.out.println("You inserted a quarter");} else if (state == SOLD_OUT) {System.out.println("You can't insert a quarter, the machine is sold out");} else if (state == SOLD) {System.out.println("Please wait, we're already giving you a gumball");}}public void ejectQuarter() {if (state == HAS_QUARTER) {System.out.println("Quarter returned");state = NO_QUARTER;} else if (state == NO_QUARTER) {System.out.println("You haven't inserted a quarter");} else if (state == SOLD) {System.out.println("Sorry, you already turned the crank");} else if (state == SOLD_OUT) {System.out.println("You can't eject, you haven't inserted a quarter yet");}}public void turnCrank() {if (state == SOLD) {System.out.println("Turning twice doesn't get you another gumball!");} else if (state == NO_QUARTER) {System.out.println("You turned but there's no quarter");} else if (state == SOLD_OUT) {System.out.println("You turned, but there are no gumballs");} else if (state == HAS_QUARTER) {System.out.println("You turned...");state = SOLD;dispense();}}private void dispense() {if (state == SOLD) {System.out.println("A gumball comes rolling out the slot");count = count - 1;if (count == 0) {System.out.println("Oops, out of gumballs!");state = SOLD_OUT;} else {state = NO_QUARTER;}} else if (state == NO_QUARTER) {System.out.println("You need to pay first");} else if (state == SOLD_OUT) {System.out.println("No gumball dispensed");} else if (state == HAS_QUARTER) {System.out.println("No gumball dispensed");}}public void refill(int numGumBalls) {this.count = numGumBalls;state = NO_QUARTER;}public String toString() {StringBuffer result = new StringBuffer();result.append("\nMighty Gumball, Inc.");result.append("\nJava-enabled Standing Gumball Model #2004\n");result.append("Inventory: " + count + " gumball");if (count != 1) {result.append("s");}result.append("\nMachine is ");if (state == SOLD_OUT) {result.append("sold out");} else if (state == NO_QUARTER) {result.append("waiting for quarter");} else if (state == HAS_QUARTER) {result.append("waiting for turn of crank");} else if (state == SOLD) {result.append("delivering a gumball");}result.append("\n");return result.toString();}
}

以上的代码最大的问题就是没有遵守开发-关闭原则,一遇到新的需求(投币后有10%的概率出现“赢家”状态,给出2颗糖果)就需要修改源代码,重新整理所有代码的逻辑。

重构后的代码理念:

  • 定义一个State接口,糖果机器的每个动作都在接口中有一个对应的方法。
  • 为机器中的每个状态实现一个状态类。这些类将负责在对应状态下进行机器的行为。
  • 将动作委托到状态类。

// 每种状态的各个方法的行为都不一样NoQuarterState
{insertQuarter() // 转到HasQuarterStateejectQuarter()  // 未投入25分钱turnCrank()     // 未投入25分钱,转动曲柄无效dispense()      // 未投入25分钱,不能分发糖果
}

在新的糖果机中,我们不使用静态整数,而使用state对象。

public class GumballMachine {// 所有的状态对象都在构造器中创建并赋值State soldOutState;State noQuarterState;State hasQuarterState;State soldState;State state;int count = 0;public GumballMachine(int numberGumballs) {soldOutState = new SoldOutState(this);noQuarterState = new NoQuarterState(this);hasQuarterState = new HasQuarterState(this);soldState = new SoldState(this);this.count = numberGumballs;if (numberGumballs > 0) {state = noQuarterState;} else {state = soldOutState;}}public void insertQuarter() {state.insertQuarter();}public void ejectQuarter() {state.ejectQuarter();}public void turnCrank() {state.turnCrank();state.dispense();}void releaseBall() {System.out.println("A gumball comes rolling out the slot...");if (count > 0) {count = count - 1;}}int getCount() {return count;}void refill(int count) {this.count += count;System.out.println("The gumball machine was just refilled; its new count is: " + this.count);state.refill();}void setState(State state) {this.state = state;}public State getState() {return state;}public State getSoldOutState() {return soldOutState;}public State getNoQuarterState() {return noQuarterState;}public State getHasQuarterState() {return hasQuarterState;}public State getSoldState() {return soldState;}public String toString() {StringBuffer result = new StringBuffer();result.append("\nMighty Gumball, Inc.");result.append("\nJava-enabled Standing Gumball Model #2004");result.append("\nInventory: " + count + " gumball");if (count != 1) {result.append("s");}result.append("\n");result.append("Machine is " + state + "\n");return result.toString();}
}

现在我们已经可以:

  • 将每个状态的行为局部化到它自己的类中。
  • 将容易产生问题的if语句删除,以方便日后的维护。
  • 让每个状态“对修改关闭”,让糖果机“对扩展开放”(可以加入新的状态类)

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

策略模式和状态模式的类图是一样的(回去翻了下书,好像没瞅到),但

我们把策略模式想成是除了继承之外的一种弹性替代方案。如果使用继承定义一个类的行为,则会被这个行为困住,很难修改。

状态模式是不用在context中放置许多条件判断的替代方案。通过将行为包装进状态对象中,可以通过在context内简单改变状态对象来改变context的行为。

在GumballMachine中,状态决定了下一个状态应该是什么。ConcreteState总是决定接下来的状态是什么吗?

状态转换是固定的时候,就适合放在Context中。转换是更动态的适合,通常就会放在状态类中。

// GumballMachine的修改和WinnerState的实现是很简单的
// 这里就只将HasQuarterState列出import java.util.Random;public class HasQuarterState implements State {Random randomWinner = new Random(System.currentTimeMillis());GumballMachine gumballMachine;public HasQuarterState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}public void insertQuarter() {System.out.println("You can't insert another quarter");}public void ejectQuarter() {System.out.println("Quarter returned");gumballMachine.setState(gumballMachine.getNoQuarterState());}public void turnCrank() {System.out.println("You turned...");int winner = randomWinner.nextInt(10);if ((winner == 0) && (gumballMachine.getCount() > 1)) {gumballMachine.setState(gumballMachine.getWinnerState());} else {gumballMachine.setState(gumballMachine.getSoldState());}}public void dispense() {System.out.println("No gumball dispensed");}public void refill() { }public String toString() {return "waiting for turn of crank";}
}

------------------

相关文章:

设计模式之十:状态模式

状态模式通过改变对象内部的状态来帮助对象控制自己的行为。 这是一张状态图,其中每个圆圈都是一个状态。 最简单,第一反应的实现就是使用一个变量来控制状态值,并在方法内书写条件代码来处理不同情况。 package headfirst.designpatterns.…...

Python 的运算符和语句(条件、循环、异常)基本使用指南

运算符的分类 算数运算符赋值运算符复合赋值运算符比较运算符逻辑运算符 算数运算符 运算符描述实例加1 1 输出结果为 2-减1 - 1 输出结果为 0*乘2 * 2 输出结果为 4/除10 / 2 输出结果为 5//整除9 // 4 输出结果为 2%取余9 % 4 输出结果为 1**指数2 ** 4 输出结果为 16&…...

文心一言 VS 讯飞星火 VS chatgpt (94)-- 算法导论9.2 3题

三、用go语言,给出 RANDOMIZED-SELECT的一个基于循环的版本。 文心一言: 在循环中实现 RANDOMIZED-SELECT 的一个版本可以如下所示。这个版本使用 Go 语言编写,利用随机化来选择一个元素,并在循环中不断地调整选择的元素&#x…...

React中组件通信01——props

React中组件通信01——props 1. 父传子——props1.1 简单例子——props1.2 props 可以传递任何数据1.2.1 传递数字、对象等1.2.2 传递函数1.2.3 传递模版jsx 2. 子传父 子传子——props2.1 父传子——传递函数2.2 子传父——通过父传子的函数实现2.3 优化 子传子(…...

接口测试——接口协议抓包分析与mock_L1

目录: 接口测试价值与体系常见的接口协议接口测试用例设计postman基础使用postman实战练习 1.接口测试价值与体系 接口测试概念 接口:不同的系统之间相互连接的部分,是一个传递数据的通道接口测试:检查数据的交换、传递和控制…...

四种常用的自动化测试框架

一直想仔细研究框架,写个流水账似的测试程序不难,写个低维护成本的测试框架就很难了,所以研究多种测试框架还是很有必要的,知道孰优孰劣,才能在开始编写框架的时候打好基础,今天读到了KiKi Zhao的翻译文章&…...

Fuxploider:一款针对文件上传漏洞的安全检测与研究工具

Fuxploider:一款针对文件上传漏洞的安全检测与研究工具 1.概述2. 工具使用1.概述 Fuxploider是一款功能强大的开源渗透测试工具,该工具专门针对文件上传漏洞而设计,可以帮助广大研究人员以自动化的方式检测和利用目标站点文件上传表单中的安全问题 由于该工具基于Python 3…...

Unity 安装及运行MLAgents

1、下载ML-Agents 下载地址 GitHub - Unity-Technologies/ml-agents: The Unity Machine Learning Agents Toolkit (ML-Agents) is an open-source project that enables games and simulations to serve as environments for training intelligent agents using deep reinfo…...

LightDB-A 兼容oracle支持mod操作符

LightDB-A 兼容oracle支持mod操作符 LightDB-A 为了兼容oracle,从23.3版本开始支持mod操作符,其语义同 ‘%’ 操作符,使用案例如下: select 5 mod 2;?column? ----------1 (1 row)select 0 % 0; ERROR: division by zerosel…...

SpringMVC之自定义注解

目录 一、Java注解 1.1 注解简介 1.2 注解分类 1.3 JDK基本注解 1.4 JDK元注解 1.5 自定义注解 1.5.1 标记注解 1.5.2 元数据注解 1.6 如何自定义注解 二、自定义注解的基本案例 2.1 案例一(获取类、方法以及属性上的注解) 2.1.1 Ingerited的…...

QT:使用普通按钮、网格布局管理器、标签、行编辑器、水平布局管理器、垂直布局管理器做一个小项目

widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QPushButton> //普通按钮 #include <QGridLayout> //网格布局管理器 #include <QLabel> //标签 #include <QLineEdit> //行编辑器 #include <QHBoxLayo…...

【小沐学写作】程序员必备技能:在线协作文档汇总

文章目录 1、简介2、微软Office在线文档2.1 功能简介2.2 使用费用2.3 用户体验 3、石墨文档3.1 功能简介3.2 使用费用 4、腾讯文档4.1 功能简介4.2 使用费用 5、语雀5.1 功能简介5.2 使用费用 6、飞书6.1 功能简介6.2 使用费用 7、印象笔记7.1 功能简介7.2 使用费用 结语 1、简…...

「工具|数据接口」免费公开的REST API 如何借助github搭建自己的fake API接口

本文主要介绍日常开发、测试、教学或者分享中&#xff0c;可能遇到的模拟数据问题。分享免费开发的测试数据接口&#xff0c;以及如何利用github快速搭建定制化的接口数据&#xff0c;避免使用真实数据的风险以及自己现编数据的麻烦。 文章目录 一、场景说明二、免费公开的Fak…...

leetcode 18. 四数之和

给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&#xff0c;则认为两个四元组重复&#xff09;&#xff1a; 0 < a,…...

树上背包问题动态规划

目录 树状动态规划概述 示例 求解思路 树状动态规划概述 树状动态规划&#xff08;Tree DP&#xff09;是一种在树结构上进行动态规划的方法。在树状DP中&#xff0c;我们利用树的特殊结构性质&#xff0c;通过递归地向下更新子节点的状态&#xff0c;最终得到整个树的最…...

linux查看进程对应的线程(数)

首先&#xff0c;top或ps查看进程列表&#xff0c;确定要查看的进程pid&#xff0c;如下面40698 查看进程的线程情况 查看进程&#xff1a;top -p 40698 查看线程&#xff1a;top -p 40698 -d 3 -H 其中-d是刷新频率 可看到此进程共211个线程&#xff0c;运行中的是211个。…...

Python中的桌面应用开发库有哪些?

Python中有几个受欢迎的桌面应用开发库。以下是其中一些&#xff1a; Tkinter&#xff1a;这是Python的标准GUI库&#xff0c;它提供了构建简单的桌面应用程序的基本组件和功能。 PyQt&#xff1a;这是一个成熟的、功能强大的跨平台图形用户界面框架&#xff0c;它是Python绑定…...

【大数据】Neo4j 图数据库使用详解

目录 一、图数据库介绍 1.1 什么是图数据库 1.2 为什么需要图数据库 1.3 图数据库应用领域 二、图数据库Neo4j简介 2.1 Neo4j特性 2.2 Neo4j优点 三、Neo4j数据模型 3.1 图论基础 3.2 属性图模型 3.3 Neo4j的构建元素 3.3.1 节点 3.3.2 属性 3.3.3 关系 3.3.4 标…...

Windows11系统C盘用户文件夹下用户文件夹为中文,解决方案

说明&#xff1a; 1. 博主电脑为Windows11操作系统&#xff0c;亲测有效&#xff0c;修改后无任何影响&#xff0c;软件都可以正常运行&#xff01; 2. Windows10系统还不知道可不可行&#xff0c;因为Windows11的计算机管理中没有本地用户和组&#xff0c;博主在csdn上看到很…...

Python正则表达式(re)

正则表达式&#xff0c;又称规则表达式,&#xff08;Regular Expression&#xff0c;在代码中常简写为regex、regexp或RE&#xff09;&#xff0c;是一种文本模式&#xff0c;包括普通字符&#xff08;例如&#xff0c;a 到 z 之间的字母&#xff09;和特殊字符&#xff08;称为…...

基于大模型的 UI 自动化系统

基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...