【状态模式】拯救if-else堆出来的屎山代码

前言

我想大家平时都在开发重都遇见过屎山代码,这些屎山代码一般都是由于复杂且庞大的if-else造成的,状态模式,是一种很好的优化屎山代码的设计模式,本文将采用两个业务场景的示例来讲解如何使用状态模式拯救屎山代码。

目录

前言

1.网购业务场景

1.1.需求

1.2.if else的实现

1.3.状态模式的实现

2.电梯业务场景

2.1.需求

2.2.if else的实现

2.3.状态模式的实现


1.网购业务场景

1.1.需求

我们来假设一个网购的业务场景,需求如下:

  • 流程为付款、再发货、在收货,流程必须按照以上顺序,也就是说发货后不能支付、收货后不能发货和支付

  • 付款后不能重复付款、发货后不能重复发货、收货后不能重复收货

1.2.if else的实现

这里我们设计一个Order订单类,用int型的state来表示状态,当然也可以用一个枚举类来表示状态会更规范一点,这里为了方便而已。

public class Order {//1 未付款//2 已付款//3 未发货//4 已发货//5 未收货//6 已收货private int state;
​public int getState() {return state;}
​public void setState(int state) {this.state = state;}
}

以收货方法为例,业务逻辑实现出来会是:

public void receive(Order order){if(order.getState()==2){if(order.getState()==4){if(order.getState()==5){System.out.println("收货成功");}else{System.out.println("已收货,无法重复收货");}}else{System.out.println("未发货");}}else{System.out.println("未付款");}
}

可以看到一座小屎山代码已经初具规模,但凡状态再多一点、业务逻辑再复杂一点,这座屎山将会基本不具备可读性。

1.3.状态模式的实现

其实仔细观察可以发现,很多时候状态往往是和实体的行为是相关的。之所以引入状态,我们是希望实体在不同的状态时呈现出不同的行为。

以上面的场景为例,在支付状态下,我们希望实体能呈现出支付相关的能力;在发货状态下呈现出发货相关的能力;在收货状态下呈现出收货相关的能力......

所以完全可以把状态和能力封装在一起,从而省掉外界的if-else判断,这就是所谓的状态模式。

状态模式总结起来一句话:

实体在不同的状态,拥有不同的行为。

作用是:

可以省掉大量判断条件带来的if-else逻辑分支,使得代码更简洁易读。

接下来我们用状态模式去改写之前的代码。

首先总结一下实体类会有的行为有哪些,其实就是付款、发货、收货,也就是三个方法,为了代码的规范,可以抽象出行为接口,当然不抽象也可以,仁者见仁智者见智。

public interface OrderState{void pay(Order order);void ship(Order order);void receive(Order order);
}

接下来总结一下系统里面的状态,订单有三个维度的六种状态,分别是:

  • 付款状态

    • 未付款

    • 已付款

  • 发货状态

    • 未发货

    • 已发货

  • 收货状态

    • 未收货

    • 已收货

于是可以得到有三个状态实体。

将状态和行为绑定,可以得到以下三个状态实体。

支付状态实体:

public class PayState implements OrderState{public void pay(Order order) {System.out.println("已支付,不能再次支付!");}
​public void ship(Order order) {order.setOrderState(new ShipState());System.out.println("已发货!");}
​public void receive(Order order) {System.out.println("未发货!不能收货!");}
}

发货状态实体:

public class ShipState implements OrderState{public void pay(Order order) {System.out.println("已发货!禁止重复支付!");}
​public void ship(Order order) {System.out.println("已经发货!禁止重复支付");}
​public void receive(Order order) {order.setOrderState(new ReceiveState());System.out.println("收货成功!");}
}

收货状态实体

public class ReceiveState implements OrderState{public void pay(Order order) {System.out.println("已收货,不能再次支付!");}
​public void ship(Order order) {System.out.println("已收货,不能再次发货!");}
​public void receive(Order order) {System.out.println("已收货,不能再次收货!");}
}

测试代码:

public class Test {public static void main(String[] args) {Order order=new Order();//初始状态未待支付order.setOrderState(new PayState());order.pay();order.ship();order.receive();}
}

测试结果:

2.电梯业务场景

2.1.需求

我们考虑一个简单的电梯系统,其中有以下状态:

  1. 停止状态(StoppedState): 当电梯处于停止状态时,它可以接受移动到指定楼层的请求。

  2. 上升状态(MovingState): 当电梯处于上升状态时,它不能响应移动请求,因为它正在上升。

  3. 下降状态(MovingState): 当电梯处于下降状态时,它也不能响应移动请求,因为它正在下降。

规则:

  • 当电梯处于停止状态时,它可以接受移动到指定楼层的请求,并切换到移动状态(上升或下降)。

  • 当电梯处于上升状态或下降状态时,它不能接受移动请求,而是提示当前正在移动。

  • 电梯在移动过程中不能响应其他移动请求,直到它到达指定楼层并切换到停止状态。

在上面的业务情景中,我们通过使用状态模式对电梯系统进行了优化。每个状态(停止状态和移动状态)都对应一个状态类,并定义了在该状态下的行为。电梯状态的切换由上下文类(ElevatorStateContext)来管理,它负责在不同状态下执行不同的行为,并根据状态的变化进行切换。通过使用状态模式,我们将状态切换逻辑封装到不同的状态类中,使代码更加模块化和可扩展。

2.2.if else的实现

class ElevatorIfElse {private String state = "停止";private int currentFloor = 1;
​public void setState(String newState) {state = newState;}
​public void moveToFloor(int floor) {if (state.equals("停止")) {System.out.println("电梯从 " + currentFloor + " 楼移动到 " + floor + " 楼");currentFloor = floor;} else if (state.equals("上升")) {System.out.println("电梯正在上升,不能移动");} else if (state.equals("下降")) {System.out.println("电梯正在下降,不能移动");}}
}
​
public class MainIfElse {public static void main(String[] args) {ElevatorIfElse elevator = new ElevatorIfElse();
​elevator.moveToFloor(5);elevator.setState("上升");elevator.moveToFloor(3);elevator.moveToFloor(7);elevator.setState("停止");elevator.moveToFloor(2);}
}

2.3.状态模式的实现

interface ElevatorState {void moveToFloor(ElevatorStateContext context, int floor);
}
​
class StoppedState implements ElevatorState {@Overridepublic void moveToFloor(ElevatorStateContext context, int floor) {System.out.println("电梯从 " + context.getCurrentFloor() + " 楼移动到 " + floor + " 楼");context.setCurrentFloor(floor);context.setState(new MovingState());}
}
​
class MovingState implements ElevatorState {@Overridepublic void moveToFloor(ElevatorStateContext context, int floor) {System.out.println("电梯正在移动,不能移动");}
}
​
class ElevatorStateContext {private ElevatorState state;private int currentFloor = 1;
​public ElevatorStateContext() {this.state = new StoppedState();}
​public void setState(ElevatorState state) {this.state = state;}
​public void moveToFloor(int floor) {state.moveToFloor(this, floor);}
​public int getCurrentFloor() {return currentFloor;}
​public void setCurrentFloor(int currentFloor) {this.currentFloor = currentFloor;}
}
​
public class MainStatePattern {public static void main(String[] args) {ElevatorStateContext context = new ElevatorStateContext();
​context.moveToFloor(5);context.setState(new MovingState());context.moveToFloor(3);context.moveToFloor(7);context.setState(new StoppedState());context.moveToFloor(2);}
}

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

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

相关文章

【Axure高保真原型】通过输入框动态控制环形图

今天和大家分享通过输入框动态控制环形图的原型模板,在输入框里维护项目数据,可以自动生成对应的环形图,鼠标移入对应扇形,可以查看对应数据。使用也非常方便,只需要修改输入框里的数据,或者复制粘贴文本&a…

简单记录牛客top101算法题(初级题C语言实现)BM17 二分查找 BM21 旋转数组的最小数字 BM23 二叉树的前序遍历

1. BM17 二分查找 要求:给定一个 元素升序的、无重复数字的整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标(下标从 0 开始),否则返回 -1。 输入&#xff1a…

【云原生】K8S存储卷:PV、PVC详解

目录 一、emptyDir存储卷二、hostPath存储卷三、nfs共享存储卷四、PVC 和 PV4.1 NFS使用PV和PVC4.2创建动态PV 一、emptyDir存储卷 容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题。首先,当容器崩溃时,ku…

UG NX二次开发(C++)-PK函数创建一条圆弧曲线

文章目录 1、前言2、创建一个项目3、添加头文件4、在do_it中添加创建圆曲线的源代码5、调用dll6、再创建一个长方体验证1、前言 采用PK进行UG NX二次开发,现在看到的文章很多是直接创建实体,然后在UG NX的视图区显示出来,对于创建圆曲线的文章不多,本文讲一下PK函数创建圆…

Git 代码分支规范

目的 俗话说:没有规矩,不成方圆。遵循一个好的规章制度能让你的工作事半功倍。同时也可以展现出你做事的认真的态度以及你的专业性,不会显得杂乱无章,管理困难。Git分支规范也是一样。当遵循了某种约定的Git分支,在代…

若依框架浅浅介绍

由若依官网所给介绍可知 1、文件结构介绍 在ruoyi-admin的pom.xml文件中引入了ruoyi-framework、ruoyi-quartz和ruoyi-generatior模块,在ruoyi-framework的pom.xml文件中引入了ruoyi-system模块。 2、技术栈介绍 前端:Vue、Element UI后端&#xff1a…

第1章:绪论

科学、技术、工程、应用 科学:是什么、为什么技术:怎么做工程:怎样做的多快好省应用:怎么使用 定义 机器学习:利用经验改善系统自身的性能。 研究 智能数据分析(数据分析算法) 典型的机器…

电脑ip地址怎么改 ip地址怎么改到别的城市

一、ip地址怎么改到别的城市 1.ip地址怎么改到别的城市,1、重启WIFI路由设备 一般手机或电脑在家或公司上网时都是接入到路由器的WIFI网络,再由路由器分配上网IP地址,如果要更换上网IP那么重启路由器设备后,路由器会向网络运营商进行宽带的重新拨号,此时手机或电脑设…

【【verilog 典型电路设计之加法器树乘法器】】

verilog 典型电路设计之加法器树乘法器 加法器树乘法器 加法器树乘法器的设计思想是“移位后加”,并且加法运算采用加法器树的形式。乘法运算的过程是,被乘数与乘数的每一位相乘并且乘以相应的权值,最后将所得的结果相加,便得到了…

selenium.webdriver Python爬虫教程

文章目录 selenium安装和使用 selenium安装和使用 pip install selenium 下载对应的浏览器驱动 实例化浏览器 from selenium import webdriverbrowser webdriver.Chrome()元素定位 控制浏览器

HTB-Keeper

HTB-Keeper 信息收集80端口 lnorgaardroot 信息收集 80端口 80主页给了一个跳转的链接 跟随链接后到了一个登陆界面。 尝试搜索默认密码。 通过账号root:password登录。不知道为什么我登陆了两次才成功。 通过搜索在Admin->Users->Select里面发现了用户信息。 lno…

WS2812B————动/静态显示

一,系统架构 二,芯片介绍 1.管脚说明 2.数据传输时间 3.时序波形 4.数据传输方法 5.常用电路连接 三,代码展示及说明 驱动模块 在驱动模块首先选择使用状态机,其中包括,空闲状态,复位清空状态&#xff0c…

怎么把图片表格转换成word表格?几个步骤达成

在处理文档时,图片表格的转换是一个常见的需求。而手动输入表格是非常耗时的,因此,使用文本识别软件来自动转换图片表格可以大大提高工作效率。在本文中,我们将介绍如何使用OCR文字识别技术来将图片表格转换为Word表格。 OCR文字识…

15.3 【Linux】循环执行的例行性工作调度

相对于 at 是仅执行一次的工作,循环执行的例行性工作调度则是由 cron (crond) 这个系统服务来控制的。刚刚谈过 Linux 系统上面原本就有非常多的例行性工作,因此这个系统服务是默认启动的。另外, 由于使用者自己也可以…

栈和队列--受限制的线性表

目录 和队列的定义和特点 1.1栈的定义和特点、 1.2队列的定义和特点 1.3栈和队列的应用 2.栈的表示和操作的实现 2.1栈的类型定义 2.2顺序栈的表示和实现 2.2.1初始化 2.2.2入栈 2.2.3出栈 2.2.4取栈顶元素 2.3链栈的表示和实现 2.2.1初始化 2.2.2入栈 2.2.3出栈…

Java-运算符和控制语句(下)(基于c语言的补充)

输出到控制台 System.out.println(msg); // 输出一个字符串, 带换行 System.out.print(msg); // 输出一个字符串, 不带换行 System.out.printf(format, msg); // 格式化输出 从键盘输入 使用 Scanner 读取字符串/整数/浮点数 首先需要导入util包 自动导入util包 这里把回车看…

如何选择最佳的文件传输协议?(FTP、TFTP、Raysync)

在数字化时代,通过互联网传输文件是一项常见的任务。因此,选择适合您企业需求的文件传输协议非常重要。 文件传输协议是发送方和接收方之间的一套规则和信息。它的作用就像网络两端都能理解的一种语言,使得数据可以正确输出并带有正确的文件…

【高频面试题】JVM篇

文章目录 一、JVM组成1.什么是程序计数器2.什么是Java堆?3.能不能介绍一下方法区(元空间)4.你听过直接内存吗5.什么是虚拟机栈6.垃圾回收是否涉及栈内存?7.栈内存分配越大越好吗?8.方法内的局部变量是否线程安全?9.什么…

NIO 非阻塞式IO

NIO Java NIO 基本介绍 Java NIO 全称 Java non-blocking IO,是指 JDK 提供的新 API。从 JDK1.4 开始,Java 提供了一系列改进的输入/输出的新特性,被统称为 NIO(即 NewIO),是同步非阻塞的。NIO 相关类都被…

netty学习分享 二

操作系统IO模型与实现原理 阻塞IO 模型 应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好。如果数据没有准备好,一直等待….数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。 当调用recv()函数时,系…