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

news2024/11/26 12:42:40

前言

我想大家平时都在开发重都遇见过屎山代码,这些屎山代码一般都是由于复杂且庞大的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 {
    @Override
    public void moveToFloor(ElevatorStateContext context, int floor) {
        System.out.println("电梯从 " + context.getCurrentFloor() + " 楼移动到 " + floor + " 楼");
        context.setCurrentFloor(floor);
        context.setState(new MovingState());
    }
}
​
class MovingState implements ElevatorState {
    @Override
    public 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.coloradmin.cn/o/877878.html

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

相关文章

什么是管理的本质?

管理不是一门硬科学。与(通常)存在正确答案的化学或代数不同,管理是流动的,主观的,并且对于如何运用其原理存在不同的观点。但究竟什么是管理?大多数学者都有相同定义的变体,包括利用资源来实现…

Ubuntu系统搭建socks5代理 Danted SOCKS Proxy

本教程介绍如何在Ubuntu系统中搭建Dated Socks5 代理服务器 更新系统 apt update安装 danted软件 apt install dante-server修改该danted软件的配置文件 vi /etc/danted.conf修改后的内容为下: logoutput: syslog user.privileged: root user.unprivileged: no…

沁恒ch32V208处理器开发(五)复用与重映射

目录 概述实现方法GPIO 及其复用功能AFIO 的寄存器实现 重映射的功能限制 概述 在ch32V2xx系列处理器中,芯片引脚数从28到68不等,分为几个系列,不同型号之间外设资源有差异,所以,引脚复用和重映射功能 也不一样&#…

jmeter获取mysql数据

JDBC Connection Configuration Database URL: jdbc:mysql:// 数据库地址 /库名 JDBC Driver class:com.mysql.jdbc.Driver Username:账号 Password:密码 JDBC Request 字段含义 字段含义 Variable Name Bound to Pool 数据库连接池配置…

分类预测 | MATLAB实现GWO-BiGRU-Attention多输入分类预测

分类预测 | MATLAB实现GWO-BiGRU-Attention多输入分类预测 目录 分类预测 | MATLAB实现GWO-BiGRU-Attention多输入分类预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.GWO-BiGRU-Attention 数据分类预测程序 2.代码说明:基于灰狼优化算法(GW…

这四种订货系统不能选(二):不能独立部署

订货系统在传统批发贸易企业数字化转型中扮演着重要的角色。然而,有一种类型的订货系统并不适合选择,那就是无法独立部署的系统。 无法独立部署的订货系统意味着数据必须存放在软件厂商的服务器上。当我们选择这样的系统时,需要确保系统具备强…

C++11并发与多线程笔记 (1)

C11并发与多线程笔记(1) 1、并发、进程、线程的基本概念和综述1.1 并发1.2 可执行程序1.3 进程1.4 线程1.5 学习心得 2、并发的实现方法2.1 多进程并发2.2 多线程并发 3、C11新标准线程库 1、并发、进程、线程的基本概念和综述 1.1 并发 指在一个时间段…

Kotlin入门:面向对象编程——04

一、类和对象: 在Kotlin中,使用class关键字定义类,使用object关键字定义对象。类是对象的蓝图,用于描述对象的属性和行为,而对象则是类的一个实例。类可以包含属性(成员变量)和方法&#xff08…

SpringBoot复习:(47)ConfigFileApplicationListener

它监听ApplicationEnvironmentPreparedEvent和ApplicationPreparedEvent。 它会把配置文件中配置的内容注入到环境中去,配置文件也就生效了

Photoshop快捷键大全

Photoshop是一款非常强大的图像处理软件,它提供了许多快捷键,可以帮助用户更快地完成操作。熟练掌握这些快捷键,可以大大提高工作效率,让您更加专注于创作。 Photoshop快捷键汇总: 一、基本操作快捷键 1. 新建文档…

TX Text Control .NET Server for ASP.NET Crack

TX Text Control .NET Server for ASP.NET Crack TX Text Control.NET Server for ASP.NET是用于Web应用程序或服务的服务器端组件。它是一个完全可编程的ASP.NET文字处理引擎,提供了广泛的文字处理功能。使用TX Text Control.NET Server,程序员可以开发…

day3 TCP/UDP基础模型、多点通信、TCP开发服务器模型

1.多线程中的newfd,能否修改成全局,不行,为什么? 不能。线程之间共享附属进程的所有资源,newfd是全局变量,作用域是全局,一经更改所有线程中的newfd都会变化。 2.多线程中分支线程的newfd能否…

Android 并发编程--阻塞队列和线程池

一、阻塞队列 队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作…

TCP消息传输可靠性保证

TCP链接与断开 -- 三次握手&四次挥手 三次握手 TCP 提供面向有连接的通信传输。面向有连接是指在数据通信开始之前先做好两端之间的准备工作。 所谓三次握手是指建立一个 TCP 连接时需要客户端和服务器端总共发送三个包以确认连接的建立。在socket编程中,这一…

NLP文本匹配任务Text Matching [无监督训练]:SimCSE、ESimCSE、DiffCSE 项目实践

NLP文本匹配任务Text Matching [无监督训练]:SimCSE、ESimCSE、DiffCSE 项目实践 文本匹配多用于计算两个文本之间的相似度,该示例会基于 ESimCSE 实现一个无监督的文本匹配模型的训练流程。文本匹配多用于计算两段「自然文本」之间的「相似度」。 例如…

【ARM】Day1

作业1:思维导图 作业2: 作业3:用for循环实现1~100之间和5050

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

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

Go语言为何强大?Python性能差为何霸榜编程语言第一?

目录 Go 语言为何强大 Go、C、C、Java、python做个简单的性能比较 Python底层是如何调用C代码的 为什么python在数据科学、机器学习和自动化较其他语言有优势 对一名软件工程师而言,语言虽然只是一门工具,往往一个软件工程师身上都会写好几门&#x…

MQTT基础入门与资料收集

文章目录 目的基础入门MQTT相关软件MQTT客户端软件MQTT Broker(服务器)软件 MQTT编程总结 目的 最近项目中涉及到MQTT相关内容,在此对相关内容做个基础的梳理。 基础入门 MQTT官网: https://mqtt.org/ MQTT是用于物联网&#x…

十大经典排序算法

目录 前言 冒泡排序 选择排序 插入排序 希尔排序 归并排序 快速排序 堆排序 计数排序 桶排序 基数排序 十大排序之间的比较 总结 前言 学了数据结构之后一直没有进行文字性的总结,现在趁着还有点时间把相关排序的思路和代码实现来写一下。概念的话网上…