JS设计模式之状态模式:优雅地管理应用中产生的不同状态

news2024/11/18 7:46:58

image.png

一. 前言

在过去,我们经常使用条件语句(if-else 语句)来处理应用程序中的不同状态。然而,这种方式往往会让代码变得冗长、难以维护,并可能引入潜在的 bug。而状态模式则提供了一种更加结构化和可扩展的方法来处理状态转换。

简单来说,状态模式将每个状态封装成一个单独的类,并将状态间的转换逻辑封装在一个上下文对象中。通过这种方式,我们可以根据当前状态的不同,调用不同状态类中的方法来执行相应的逻辑。这种分离状态和行为的做法使得代码更加灵活和可扩展,并且便于我们理解和维护。

在本篇文章中,我们将探讨 JavaScript 状态模式的实现和应用,学习如何使用状态模式来避免 if-else 语句的嵌套,简化复杂状态的管理,并提高代码的可维护性。

那么接下来就让我们一步步了解如何在现有代码中引入状态模式,并考虑在实际情况中使用状态模式解决常见问题。

二. 什么是状态模式

JavaScript 状态模式是一种行为设计模式,用于通过将对象的行为和状态进行解耦,使得对象能够在不同的状态下具有不同的行为。它允许一个对象在其内部状态改变时更改其行为,而无需改变对象本身的结构。

在状态模式中,对象的行为取决于其当前状态。通过将状态封装为独立的类或对象,状态模式使得状态的变化可以独立于对象本身的变化。使用状态模式,可以将复杂的、分散的条件语句转化为状态类之间的切换,提高代码的可读性和可维护性。

状态模式通常由以下几个角色组成:

  1. 上下文环境Context):环境是拥有状态的对象,它维护一个对当前状态对象的引用,并在状态发生变化时更新自身的行为。

  2. 抽象状态State):抽象状态定义了一个接口,用于封装对象不同状态所对应的行为。

  3. 具体状态Concrete State):具体状态实现了抽象状态定义的接口,并定义了在该状态下对象的具体行为。

image.png

使用 JavaScript 状态模式可以提供以下优点:

  1. 结构清晰:将状态和行为分离,使代码结构更加清晰,易于理解和维护。

  2. 可扩展性:添加新的状态类简单,不需要修改其他代码,符合开闭原则。

  3. 低耦合:状态模式将状态的切换逻辑放在状态类中,状态之间可以独立变化,降低对象之间的耦合度。

  4. 简化条件判断:状态模式将复杂的条件语句转化为状态类之间的切换,使代码更简洁和可读。

总之,JavaScript 状态模式是一种有效的设计模式,可以帮助开发人员管理对象的状态和行为,提高代码的可维护性和可扩展性。

三. 实现方式

在 JavaScript 中,状态模式可以通过以下几个步骤来实现:

  1. 定义状态接口State Interface):在 JavaScript 中,我们可以使用类或对象字面量来定义状态接口。状态接口定义了状态类的共同方法,每个具体状态类都必须实现这些方法。

// 状态接口
class StateInterface {
  handleAction() {
    // 具体状态的行为逻辑
  }
}

// 或者使用对象字面量
const stateInterface = {
  handleAction() {
    // 具体状态的行为逻辑
  },
};
  1. 实现具体状态类Concrete State Class):具体状态类实现状态接口,并定义每个具体状态的行为逻辑。

// 具体状态类
class ConcreteStateA extends StateInterface {
  handleAction() {
    // 具体状态A的行为逻辑
  }
}

class ConcreteStateB extends StateInterface {
  handleAction() {
    // 具体状态B的行为逻辑
  }
}

// 或者使用对象字面量
const concreteStateA = {
  handleAction() {
    // 具体状态A的行为逻辑
  },
};

const concreteStateB = {
  handleAction() {
    // 具体状态B的行为逻辑
  },
};
  1. 定义上下文类Context Class):上下文类包含状态对象,并提供接口供客户端代码调用。上下文类将具体的状态转换逻辑封装在内部,通常会通过改变当前状态来触发不同的行为。

class Context {
  constructor() {
    this.state = null; // 当前状态
  }

  setState(state) {
    this.state = state;
  }

  handleAction() {
    this.state.handleAction();
  }
}
  1. 使用状态模式:在实际应用中,我们可以通过创建具体的状态对象,并将它们赋值给上下文对象来使用状态模式。

const context = new Context();

// 设置初始状态
context.setState(new ConcreteStateA());

// 调用上下文对象的方法进行具体操作
context.handleAction(); // 根据当前状态,执行对应的行为

// 状态切换
context.setState(new ConcreteStateB());
context.handleAction(); // 根据当前状态,执行对应的行为

通过以上步骤,我们可以实现 JavaScript 中的状态模式。通过封装每个具体状态为单独的类,并在上下文对象中管理状态的切换,我们可以有效地组织和管理应用程序的不同状态。

这种结构化的设计模式提高了代码的可维护性和可扩展性,同时也增加了代码的可读性和清晰度。

四. 应用场景

举一个最常见的例子,就是电商网站的订单管理,一个订单可能经历多个不同的状态,如待付款待发货运输中已完成等。状态模式可以帮助我们管理和切换这些不同的订单状态,从而处理相应的逻辑。

通过一个简单的代码示例来说明这个场景:

// 定义订单状态接口
class OrderState {
  // 将订单状态作为参数传入,以便在不同状态下执行相应的行为
  constructor(order) {
    this.order = order;
  }

  cancel() {
    throw new Error("该状态下不可取消订单");
  }

  pay() {
    throw new Error("该状态下不可支付订单");
  }

  ship() {
    throw new Error("该状态下不可发货");
  }

  // 其他可能的订单操作...
}

// 具体订单状态类
class NewOrderState extends OrderState {
  cancel() {
    console.log("订单已取消");
    this.order.setState(new CancelledOrderState(this.order));
  }

  pay() {
    console.log("订单已支付");
    this.order.setState(new PaidOrderState(this.order));
  }

  ship() {
    console.log("订单未支付,无法发货");
  }
}

class PaidOrderState extends OrderState {
  cancel() {
    console.log("订单已取消");
    this.order.setState(new CancelledOrderState(this.order));
  }

  pay() {
    console.log("订单已支付,请勿重复支付");
  }

  ship() {
    console.log("订单已发货");
    this.order.setState(new ShippedOrderState(this.order));
  }
}

class ShippedOrderState extends OrderState {
  cancel() {
    console.log("订单已发货,无法取消");
  }

  pay() {
    console.log("订单已支付,请勿重复支付");
  }

  ship() {
    console.log("订单已发货,请勿重复发货");
  }

  // 其他可能的订单操作...
}

class CancelledOrderState extends OrderState {
  cancel() {
    console.log("订单已取消,请勿重复取消");
  }

  // 其他可能的订单操作...
}

// 订单管理类
class Order {
  constructor() {
    // 设置初始状态为新订单状态
    this.state = new NewOrderState(this);
  }

  setState(state) {
    this.state = state;
  }

  cancel() {
    this.state.cancel();
  }

  pay() {
    this.state.pay();
  }

  ship() {
    this.state.ship();
  }

  // 其他可能的订单操作...
}

使用示例如下所示:

const order = new Order();

order.cancel(); // 输出:"该状态下不可取消订单"

order.pay(); // 输出:"订单已支付"

order.pay(); // 输出:"订单已支付,请勿重复支付"

order.ship(); // 输出:"订单已发货"

order.cancel(); // 输出:"订单已发货,无法取消"

在上面的代码示例中,我们首先定义了一个订单状态接口 OrderState,它包含了订单可能的操作方法。

然后,我们实现了具体的订单状态类 NewOrderStatePaidOrderStateShippedOrderStateCancelledOrderState,每个状态都覆盖了可能的操作,并在状态切换时设置相应的下一个状态。

最后,我们定义了订单管理类 Order,它包含了订单的当前状态,并提供了一些操作方法用于调用当前状态的相应操作。

通过使用状态模式,我们可以根据不同的订单状态触发相应的行为,如取消订单、支付订单、发货等。同时,我们可以轻松地添加新的状态类,并定义它们所需的行为逻辑,这使得代码结构清晰,并且易于扩展和维护。

五. 总结

JavaScript 状态模式是一种非常有用和强大的设计模式,它可以帮助开发人员管理对象的不同状态和相应的行为。通过将状态和行为分离,状态模式可以提高代码的可扩展性和复用性。

一般来说,状态模式具有许多优点,它可以使代码结构更加清晰易于理解和维护。通过将分散的、冗长的条件语句转换为状态类之间的切换,代码变得更加简洁和可读。此外,状态模式支持系统的扩展性,遵循开闭原则,方便添加新的状态类和行为。

然而,状态模式也有一些缺点。引入状态模式后,可能会增加类和对象的数量,从而增加代码的复杂度理解难度。在状态转换较为简单的场景中,引入状态模式可能并不切实际,会增加系统复杂性。

在实际应用中,要权衡状态模式的优缺点,根据具体的场景和需求进行选择。当对象有多个状态且状态之间存在复杂的转换逻辑时,状态模式是一个非常好的选择。

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

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

相关文章

支持超高分辨率图片生成,UltraPixel模型分享

UltraPixel是一种由华为诺亚方舟实验室联合香港科技大学共同开发的超高清图像合成架构,旨在生成具有丰富细节的高质量图像,其分辨率可以从1K一直延伸至6K。 UltraPixel不仅仅是一个图像放大工具,它还能在生成过程中优化细节,提升…

Golang | Leetcode Golang题解之第447题回旋镖的数量

题目: 题解: func numberOfBoomerangs(points [][]int) (ans int) {for _, p : range points {cnt : map[int]int{}for _, q : range points {dis : (p[0]-q[0])*(p[0]-q[0]) (p[1]-q[1])*(p[1]-q[1])cnt[dis]}for _, m : range cnt {ans m * (m - 1)…

Go实现RabbitMQ消息模式

【目标】 go实现RabbitMQ简单模式和work工作模式 go实现RabbitMQ 消息持久化和手动应答 go实现RabbitMQ 发布订阅模式 go使用MQ实现评论后排行榜更新 1. go实现简单模式 编写路由实现生产消息 实现生产消息 MQ消息执行为命令行执行,所以创建命令行执行函数mai…

react-native-Windows配置

一:官网: React Native for Windows macOS Build native Windows & macOS apps with Javascript and React 二:安装依赖 需要以管理员身份运行powershell,然后粘贴下面代码,注意:要安装淘宝镜像,要…

JAVA线程基础二——锁的概述之乐观锁与悲观锁

乐观锁与悲观锁 乐观锁和悲观锁是在数据库中引入的名词,但是在并发包锁里面也引入了类似的思想,所以这里还是有必要讲解下。 悲观锁指对数据被外界修改持保守态度,认为数据很容易就会被其他线程修改,所以在数据被处理前先对数据进行加锁&…

[Redis][典型运用][分布式锁]详细讲解

目录 0.什么是分布式锁1.分布式锁的基础实现2.引入过期时间3.引入校验ID4.引入Lua5.引入Watch Dog(看门狗)6.引入Redlock算法7.其他功能 0.什么是分布式锁 在⼀个分布式的系统中,也会涉及到多个节点访问同⼀个公共资源的情况,此时就需要通过锁来做互斥控…

一拖二快充线:单接与双接的多场景应用

在当代社会,随着智能手机等电子设备的普及,充电问题成为了人们关注的焦点。一拖二快充线作为一种创新的充电解决方案,因其便捷性与高效性而受到广泛关注。本文将深入探讨一拖二快充线的定义、原理以及在单接与双接手机场景下的应用&#xff0…

数字图像处理:空间域滤波

1.数字图像处理:空间域滤波 1.1 滤波器核(相关核)与卷积 图像上的邻域计算 线性空间滤波的原理 滤波器核(相关核)是如何得到的? 空间域的卷积 卷积:滤波器核与window中的对应值相乘后所有…

touch命令:创建文件,更新时间戳

一、命令简介 ​touch​ 命令在 Linux 和其他类 Unix 系统中用于创建空白文件或者更新已存在文件的时间戳。如果指定的文件不存在,touch​ 命令会创建一个空白文件;如果文件已经存在,touch​ 命令会更新文件的访问时间和修改时间&#xff0c…

誉天Linux云计算课程学什么?为什么保障就业?

一个IT工程师相当于干了哪些职业? 其中置顶回答生动而形象地描绘道: 一个IT工程师宛如一个超级多面手,相当于——加班狂程序员测试工程师实施工程师网络工程师电工装卸工搬运工超人。 此中酸甜苦辣咸,相信很多小伙伴们都深有体会。除了典…

用开源软件制作出精美的短视频#视频编辑

从前,有一个叫做创意森林的地方,住着各种各样的编辑精灵。一天,视频编辑精灵们发现了一本神秘的论文,里面写满了如何利用前沿的AI技术来提升他们的工作效率。于是,精灵们开始学习使用LLM和LLaVA,像魔法一样…

《企业实战分享 · 开发技术栈选型》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…

02Cesium中常用的鼠标事件

文章目录 02Cesium中常用的鼠标事件1、左键单击事件2、左键双击事件3、左键按下事件4、左键弹起事件5、中键按下事件6、中键弹起事件7、鼠标移动事件8、右键单击事件9、右键按下事件10、右键弹起事件11、鼠标滚轮事件具体在代码中的应用如下所示 02Cesium中常用的鼠标事件 Ces…

windows下安装rabbitMQ并开通管理界面和允许远程访问

如题,在windows下安装一个rabbitMQ server;然后用浏览器访问其管理界面;由于rabbitMQ的默认账号guest默认只能本机访问,因此需要设置允许其他机器远程访问。这跟mysql的思路很像,默认只能本地访问,要远程访…

《深度学习》OpenCV 图像拼接 拼接原理、参数解析、案例实现

目录 一、图像拼接 1、直接看案例 图1与图2展示: 合并完结果: 2、什么是图像拼接 3、图像拼接步骤 1)加载图像 2)特征点检测与描述 3)特征点匹配 4)图像配准 5)图像变换和拼接 6&am…

鸿蒙harmonyos next flutter通信之BasicMessageChannel获取app版本号

本文将通过BasicMessageChannel获取app版本号,以此来演练BasicMessageChannel用法。 建立channel flutter代码: //建立通道 BasicMessageChannel basicMessageChannel BasicMessageChannel("com.xmg.basicMessageChannel",StringCodec());…

系统工程 > 霍尔三维结构

简介 霍尔三维结构模型是由美国系统工程专家霍尔(A.D.Hall)在1969年提出的一种系统工程方法论,它集中体现了系统工程方法的系统化、综合化、最优化、程序化和标准化等特点 。该模型将系统工程整个活动过程分为前后紧密衔接的七个阶段和七个步…

MySQL的驱动安装

1、下载并安装MySQL 下载地址: 建议在下列框中选择LTS长期支持版本,下载对应的MSI安装文件。 安装完成后,将MySQL的环境bin路径添加到环境变量中。 可以运行MySQL Configurator进行配置,主要设置密码,并初始化。其余…

机器学习课程学习周报十四

机器学习课程学习周报十四 文章目录 机器学习课程学习周报十四摘要Abstract一、机器学习部分1. EM算法与高斯混合模型2. 概率论复习(三) 总结 摘要 本周的学习重点是EM算法与高斯混合模型的应用。单高斯模型无法有效拟合多峰数据分布,因此引…

论文精读:拓扑超导体PdBi2Te4和PdBi2Te5计算

npj Computational Materials (2023) 9:188 ; https://doi.org/10.1038/s41524-023-01144-y 摘要节选 超导拓扑金属(SCTMs)近年来成为一种很有前途的量子计算拓扑超导(TSC)和马约拉纳零模式平台。 本文提出了一种通过将超导单元嵌入到拓扑绝缘体中来设计sctm的策略。还编制了…