【状态模式】拯救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.hqwc.cn/news/66615.html

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

相关文章

【Android Framework系列】第10章 PMS之Hook实现广播的调用

1 前言 前面章节我们学习了【Android Framework系列】第4章 PMS原理我们了解了PMS原理,【Android Framework系列】第9章 AMS之Hook实现登录页跳转我们知道AMS可以Hook拦截下来实现未注册Activity页面的跳转,本章节我们来尝试一下HookPMS实现广播的发送。…

2023上半年京东手机行业品牌销售排行榜(京东数据平台)

后疫情时代,不少行业都迎来消费复苏,我国智能手机市场在今年上半年也实现温和的复苏,手机市场的出货量回暖。 根据鲸参谋平台的数据显示,2023年上半年,京东平台上手机的销量为2830万,环比增长约4%&#xf…

ORACLE行转列、列转行实现方式及案例

ORACLE行转列、列转行实现方式及案例 行转列案例方式1.PIVOT方式2.MAX和DECODE方式3.CASE WHEN和GROUP BY 列转行案例方式1.UNPIVOT方式2.UNION ALL 行转列 案例 假设我们有一个名为sales的表,其中包含了产品销售数据。表中有三列:product(…

六轴机械臂码垛货物堆叠仿真

六轴机械臂码垛货物堆叠仿真 1、建立模型与仿真 clear,clc,close all addpath(genpath(.)) %建立模型参数如下: L(1) Link( d, 0.122, a , 0 , alpha, pi/2,offset,0); L(2) Link( d, 0.019 , a ,0.408 , alpha, 0,offset,pi/2); L(3) Link( d, …

汇编指令练习

1.大小比较(循环) start: /*mov r0,#0x9mov r1,#0xfb LoopLoop:cmp r0,r1beq stopsubhi r0,r0,r1subcc r1,r1,r0b Loop stop:b stop.end 仿真图 2. 1到100之和 start:mov r0,#0x1mov r1,#0x0b sum sum:add r1,r1,r0add r0,r0,#0x1cmp r0,#0x65beq sto…

HarmonyOS NEXT新能力,一站式高效开发HarmonyOS应用

2023年8月6日华为开发者大会2023(HDC.Together)圆满收官,伴随着HarmonyOS 4的发布,华为向开发者发布了汇聚所有最新开发能力的HarmonyOS NEXT开发者预览版,并分享了围绕“一次开发,多端部署” “可分可合&a…

k8s service

1、认识Service 程序在容器中、容器在Pod中,可以通过pod的ip来访问应用程序,但是podIP会随着创建销毁而改变。由此,Service出现: Service会对提供同一个服务的多个pod进行聚合,并且提供一个统一的入口地址。通过访问…

new BigDecimal(double val)注意事项 / JWT解析BigDecimal类型数据

前言: 公司项目中有一个板块需要解析JWT令牌获取载荷里面封装的数据,遇到要解析一个BigDecimal类型的数据 问题发现过程: 正常来说,我们解析一个JWT令牌的步骤如下: public static Claims getDataFromToken(String tok…

探索 C++ 标准库:std::string 库函数用法示例

目录 引言 一、构造函数 1.1 string() 1.2 string (const string& str) 1.3 string (const string& str, size_t pos, size_t len npos) 1.4 string (const char* s) 1.5 string (const char* s, size_t n) 1.6 string (size_t n, char c)​ 二、容…

EasyPoi导出 导入(带校验)简单示例 EasyExcel

官方文档 : http://doc.wupaas.com/docs/easypoi pom的引入: <!-- easyPoi--><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter</artifactId><version>4.0.0</version></dep…

Grounding DINO:根据文字提示检测任意目标

文章目录 1. 背景介绍2. 方法创新2.1 Feature Extraction and Enhancer2.2 Language-Guided Query Selection2.3 Cross-Modality Decoder2.4 Sub-Sentence Level Text Feature2.5 Loss Function3. 实验结果3.1 Zero-Shot Transfer of Grounding DINO3.2 Referring Object Detec…

远程运维大批量IT设备?向日葵批量部署、分组授权与安全功能解析

数字化转型的不断推进&#xff0c;给予了企业全方位的赋能&#xff0c;但任何发展都伴随着成本与代价&#xff0c;比如在数字化转型过程中企业内部办公与外部业务所需的不断增加的IT设备数量&#xff0c;就为日常的运维工作提出了更大的挑战。 针对企业面对海量IT设备时的运维…