设计模式之十:状态模式

news2024/12/23 8:13:57

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

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

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

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() // 转到HasQuarterState
    ejectQuarter()  // 未投入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";
	}
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1018277.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微服务架构介绍

系统架构的演变 1、技术架构发展历史时间轴 ①单机垂直拆分:应用间进行了解耦,系统容错提高了,也解决了独立应用发布的问题,存在单机计算能力瓶颈。 ②集群化负载均衡可有效解决单机情况下并发量不足瓶颈。 ③服务改造架构 虽然系…

操作系统备考学习 day3 (2.1.1 - 2.1.6)

操作系统备考学习 day3 二、进程与线程2.1 进程与线程2.1.1 进程的概念和特征2.1.2 进程的状态与转换2.1.3 进程的组织2.1.4 进程控制2.1.5 进程间通信(IPC)2.1.6 线程和多线程模型 二、进程与线程 2.1 进程与线程 2.1.1 进程的概念和特征 进程&#…

通达OA漏洞检查工具V1.3

​注意 注意:通达OAv11.6前台任意文件删除任意文件上传漏洞会删除auth.inc.php,这可能会损坏OA系统谨慎操作 工具更新 本工具webshell采用蚁剑连接,密码均为x TongdaOATool本工具支持以下漏洞的验证和利用: 1、通达OA任意用户…

Vue通过ref修改 <el-input-number> 增减按钮的样式

Vue 为一个 <el-input-number> 设置了ref为‘inputNumberRef’, 通过这个ref获取<el-input-number>组件中的增、减按钮所在的<i>标签&#xff0c;并将它们的class分别改为el-icon-plus 和 el-icon-minus。 可以通过以下代码实现&#xff1a; <template&g…

Long类型雪花算法ID返回前端后三位精度缺失问题解决

目录 一、问题描述二、问题复现1.Maven依赖2.application.yml 配置3.DemoController.java4.snowflakePage.html 页面5.DemoControllerAdvice.java 监听6.问题复现 三、原因分析四、问题解决方案一方案二 一、问题描述 Java 后端使用雪花算法生成 Long 类型的主键 ID&#xff0…

【Unity】万人同屏, 从入门到放弃之——Entities 1.0.16性能测试

当前测试使用的Entities版本为1.0.16 Unity Entities 1.0.16使用方法&#xff1a; Create a component for the spawner example | Entities | 1.0.16 1. 创建SubScene 2. 在SubScene下创建挂载Authoring脚本&#xff1a; Authoring是MonoBehaviour脚本&#xff0c;主要用来…

网页的快捷方式打开自动全屏--Chrome、Firefox 浏览器相关设置

Firefox 的全屏方式与 Chrome 不同&#xff0c;Chrome 自带全屏模式以及APP模式&#xff0c;通过简单的参数即可设置&#xff0c;而Firefox暂时么有这个功能&#xff0c;Firefox 的全屏功能可以通过全屏插件实现。 全屏模式下&#xff0c;按 F11 不会退出全屏&#xff0c;鼠标…

【微信小程序】文章样式,标题样式,及设置背景~

| background-size 设置背景图片大小。图片可以保有其原有的尺寸&#xff0c;或者拉伸到新的尺寸&#xff0c;或者在保持其原有比例的同时缩放到元素的可用空间的尺寸。 | background-size: cover;适配屏幕大小 文章样式&#xff0c;标题样式&#xff0c;及设置背景~ index.w…

AGV小车、机械臂协同作业实战02-开源opentcs 项目调研

这个项目是从0到1 的一个过程&#xff0c;碰到这种项目给我的第一反应就是先查查有没有轮子&#xff0c;往往站在巨人的肩膀上 事情就会变得简单多了。各种搜索后发现网站这类的项目少之又少&#xff0c;唯一一个值得参考的项目 就是Opentcs 了&#xff0c;而且这个项目最近也在…

echarts-图表(非常规图开发记录)

echarts-图表&#xff08;非常规图开发记录&#xff09; 环形刻度图横向左右柱形图3D饼图渐变柱子-柱状图3D柱状图雷达图动态滚动图-并加图片 以下图表数据源&#xff0c;allData.value 均为如下格式 [{"id": "1","name": "在职在岗",…

企业邮箱功能详解:提升办公效率的多面手

企业邮箱是一种专为企业打造的电子邮件服务&#xff0c;它可以帮助企业提高工作效率、加强内部沟通和保护企业信息安全。本文将介绍企业邮箱的一些主要功能和优势。 一、邮件收发 企业邮箱提供了一个专门的电子邮件账户&#xff0c;员工可以通过这个账户收发工作相关的邮件。相…

基于SSM的校园自助洗衣系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

GIS跟踪监管系统电子围栏

GIS跟踪监管系统电子围栏 &#xff08;1&#xff09;电子围栏展示。① 显示&#xff1a;② 隐藏&#xff1a;&#xff08;2&#xff09;电子围栏修改。① 新增电子围栏。② 修改电子围栏。工具箱&#xff08;1&#xff09;测量。① 测量距离&#xff1a;② 测量面积&#xff1a…

洛科威多功能岩棉板助力节能减碳战略,推动碳达峰目标实现

国家相关部门提出了要“协同推进降碳、减污、扩绿、增长&#xff0c;推进生态优先、节约集约、绿色低碳发展”的总要求&#xff0c;未来相当长的时间里&#xff0c;降碳都将是一项重要工作&#xff0c;节能降碳是实现碳达峰碳中和的有效途径&#xff0c;也是着力解决资源环境约…

基于springboot+vue的企业面试预约管理系统

基于springbootvue的企业面试预约管理系统 预约面试管理系统&#xff0c;可以通过学生&#xff0c;企业角色进行登录 登录后可以查看发布的岗位&#xff0c;发布人&#xff0c;发布时间&#xff0c;面试时间&#xff0c;招聘时间&#xff0c;招聘单位简介等 查看用户管理信息

云组网案例分享

最近遇到了一个客户场景&#xff0c;是个非常典型的跨云组网场景&#xff0c;我们梳理了下这个客户的需求以及我们提供的解决方案&#xff0c;出于保密要求&#xff0c;文章不会涉及任何客户信息。如果您想体验我们的产品&#xff0c;可以登录我们的控制台免费使用&#xff0c;…

【Axure原型素材】扫一扫

今天和粉丝们免费分享扫一扫的原型素材&#xff0c;"扫一扫"是一项常见的移动应用功能&#xff0c;通常通过手机或平板电脑上的摄像头来扫描二维码或条形码以实现各种功能。下面是和大家分享扫一扫的常用素材~~~ 【原型效果】 【Axure原型素材】扫一扫 【原型预览】…

石油天然气工程用热轧型钢

声明 本文是学习GB-T 42678-2023 石油天然气工程用热轧型钢. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件规定了石油天然气工程用热轧型钢的订货内容、牌号表示方法、尺寸、外形、重量、技术要求、 试验方法、检验规则、包装、标志…

springboot日志配置(logback+slf4j配置)

1.为什么要配置日志 故障排查和问题分析&#xff1a; 日志记录允许开发人员和运维人员在系统发生问题或故障时追踪问题的根本原因。通过查看日志文件&#xff0c;他们可以了解系统在特定时间点发生了什么事情&#xff0c;从而更容易定位和解决问题。 性能监控和优化&#xff1a…

YOLOV7改进:在C5模块不同位置添加D-LKA Attention(同时拥有SA注意力和大卷积核的能力)

1.该文章属于YOLOV5/YOLOV7/YOLOV8改进专栏,包含大量的改进方式,主要以2023年的最新文章和2022年的文章提出改进方式。 2.提供更加详细的改进方法,如将注意力机制添加到网络的不同位置,便于做实验,也可以当做论文的创新点。 3.涨点效果:D-LKA Attention注意力机制,实现有…