设计模式之状态模式

实际开发中订单往往都包含着订单状态,用户每进行一次操作都要切换对应的状态,而每次切换判断当前的状态是必须的,就不可避免的引入一系列判断语句,为了让代码更加清晰直观,我们引入今天的主角——状态模式。

一、概念理解

假设订单状态有,下单、发货、确认收货,如果用户确认收货,在常规编程中就要判断当前用户的状态,然后再修改状态,如果这种情况下使用状态模式。

将各个状态都抽象成一个状态类,比如下单状态类、发货状态类、确认收货类,在状态类中处理相应的逻辑和控制下一个状态,在定义一个环境类,定义初始状态,并控制切换状态。

在状态模式中应该包含着三个角色:

环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,这个类持有State接口,负责保持并切换当前的状态。

抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。

具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。

以下为状态模式的类图,看起来是很直观的,理解起来也简单,我们需要说明的是状态模式的类图和策略模式的类图长的一样,但写起来状态模式比策略模式要难。

设计模式之状态模式

我们要注意这段话,在状态模式中,类的行为是基于它的状态改变的,状态之间的切换,在状态A执行完毕后自己控制状态指向状态B,状态模式是不停的切换状态执行。这也是状态模式和策略模式不一样的地方。

另外在状态模式中,状态A到B是由自己控制的,而不是由客户端来控制,这是状态模式和策略模式最显著的特征。

我们基于订单状态案例实现demo。

二、案例实现

抽象状态:

定义统一的状态切换方法

/**
 * 抽象状态
 * @author tcy
 * @Date 20-09-2022
 */
public abstract class OrderStateAbstract {
    protected Context context;

    public void setContext(Context context) {
        this.context = context;
    }
        /**
         * 状态切换
         */
    public abstract void handle();

}

具体状态-订单付款:

实现状态接口,处理相应的逻辑,并定义下一个状态

/**
 * 订单付款
 * @author tcy
 * @Date 21-09-2022
 */
public class OrderStatePay extends OrderStateAbstract {
   @Override
    public void handle() {
        System.out.println("订单已支付,执行下个状态...");
        context.changeState(new OrderStateOut());

    }
}

具体状态-订单发货

/**
 * 订单发货
 * @author tcy
 * @Date 21-09-2022
 */
public class OrderStateOut extends OrderStateAbstract {
     @Override
    public void handle() {
        System.out.println("订单已经发货,开始下一状态...");
        context.changeState(new OrderStateSubmit());
    }
}

具体状态-订单确认收货

/**
 * 订单提交
 * @author tcy
 * @Date 21-09-2022
 */
public class OrderStateSubmit extends OrderStateAbstract {
    @Override
    public void handle() {
        System.out.println("订单已经确认收货...");
    }
}

环境类:

持有最新状态,并调用具体的状态切换方法

/**
 * 环境类
 * @author tcy
 * @Date 20-09-2022
 */
public class Context {

   private OrderStateAbstract state;

    //定义环境类的初始状态
    public Context() {
        this.state = new OrderStatePay();
        state.setContext(this);
    }

    //状态切换
    public void changeState(OrderStateAbstract state) {
        this.state = state;
        this.state.setContext(this);
    }

    /**
     * 审批通过请求
     */
    public void request() {
        this.state.handle();
    }
}

客户端调用:

/**
 * @author tcy
 * @Date 20-09-2022
 */
public class Client {
    public static void main(String[] args) {

         //创建环境
        Context context = new Context();
        //订单付款
        context.request();
        //订单发货
        context.request();
        //订单付款
        context.request();

    }
}

状态模式客户端调用比较简单,由状态内部类进行状态切换。

三、总结

很多博客都将策略模式的案例代码当做状态模式来讲解,这是不正确的,读者可以参考策略模式两篇做对比学习,认真体会他们之间的区别。

在实际开发中,当控制一个对象状态转换的条件表达式过于复杂时,就可以使用状态模式把相关”判断逻辑”提取出来,用各个不同的类进行表示。

系统处于哪种情况,直接使用相应的状态类对象进行处理,这样能把原来复杂的逻辑判断简单化,消除了 if-else、switch-case 等冗余语句,代码更有层次性,并且具备良好的扩展力。

比如审批流程,我们案例也仅仅是用于订单流程做例子,在实际开发中并不会使用这种方式处理订单,因为订单的处理逻辑实际上并不是那么复杂,引入状态模式反而增加了更多的类,造成系统更加的复杂,这也是设计模式最显著的缺点。

设计模式的学习要成体系,推荐你看我往期发布的设计模式文章。

一、设计模式概述

二、设计模式之工厂方法和抽象工厂

三、设计模式之单例和原型

四、设计模式之建造者模式

五、设计模式之代理模式

六、设计模式之适配器模式

七、设计模式之桥接模式

八、设计模式之组合模式

九、设计模式之装饰器模式

十、设计模式之外观模式

十一、外观模式之享元模式

十二、设计模式之责任链模式

十三、设计模式之命令模式

十四、设计模式之解释器模式

十五、设计模式之迭代器模式

十六、设计模式之中介者模式

十七、设计模式之备忘录模式

十八、设计模式之观察者模式

Original: https://www.cnblogs.com/tianClassmate/p/16733554.html
Author: 程序员田同学
Title: 设计模式之状态模式

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/589912/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 【每日算法】算法复习一

    存在重复元素 II 给定一个整数&#x6…

    Java 2023年6月9日
    089
  • 15、lock锁

    1、lock是显示锁,手动开启和关闭,synchronized是隐式锁,出了作用域自动释放。 2、lock只有代码块锁,synchronized有代码锁和方法锁 3、使用lock锁…

    Java 2023年6月8日
    080
  • org.springframework.web.client.HttpClientErrorException: 429 Too Many Requests

    用户在在指定的时间里发送了太多的请求。用于限制速率。属于客户端异常,既客户端没有遵守服务端给定的一定频率内的限制访问次数。 一般而言,当服务端检测到客户端在短时间内频繁的尝试访问特…

    Java 2023年5月30日
    086
  • Centos静默安装Oracle11G

    环境准备 Oracle 11gR2 64位 Linux版安装包 linux.x64_11gR2_database_1of2.ziplinux.x64_11gR2_database_…

    Java 2023年6月8日
    082
  • 5-多线程

    一、创建多线程的四种方式 1.方式一:继承Thread类的方式 创建一个继承于Thread类的子类 重写Thread类的run() –> 将此线程执行的操作声明在…

    Java 2023年6月7日
    085
  • Python定时任务,三步实现自动化

    大家好,我是小一 今天的文章源自于工作中的一个小技巧,主要是涉及到日常工作的自动化处理。 如果说你每天都需要做一些重复的工作,比如出一份报告、统计一个数据、发一封邮件等等 那你完全…

    Java 2023年6月7日
    069
  • SpringBoot+SpringSecurity+SpringSession实现一个前后端分离的权限管理系统

    https://blog.csdn.net/qq_27948811/article/details/89840329 Original: https://www.cnblogs.c…

    Java 2023年5月30日
    069
  • nginx代理后端服务返回404

    nginx代理后端服务返回404 需带上host头 location ^~ /publicApi/ { rewrite ^/publicApi/(.*)$ /public/v1/$…

    Java 2023年5月30日
    070
  • NoteOfMySQL-11-权限管理

    一、权限系统 MySQL数据库中使用3种不同类型的安全检查:登录验证、授权、访问控制。 二、权限表 MySQL权限表存储在名为mysql的数据库中,常用的表有user、db、tab…

    Java 2023年6月5日
    081
  • 手把手教你如何高效落地单项目管理 | 一看既会

    在日常工作中使用协作工具你可能会遇到这些问题: 1.需求关联的代码已经发布了,但是状态还停留在待处理 2.这部分工作有固定接口人,但是每次我还要手动指定到这个人负责。 3.我负责的…

    Java 2023年6月8日
    067
  • Java HashMap 与 Hashtable的区别

    1、HashMap简介 HashMap是在JDK1.2引入的Map的实现类 HashMap核心是散列表(Hash table,也叫哈希表)散列表是根据关键码值(Key value)…

    Java 2023年6月5日
    092
  • 接上篇:Git Worktree 高级使用,这样清爽多了

    前言 上一篇文章 Git Worktree 大法真香 带大家了解了 git worktree 是如何帮助我同时在多个分支工作,并且互不影响的。但是创建 worktree 的目录位置…

    Java 2023年6月5日
    069
  • AMD Software꞉ Adrenalin Edition闪退问题

    是因为连接了两个显示器的问题,拔掉其中一个显示器的接头,就可以正常使用了,至于往深了的问题,我就不知道了,反正这个情况可以给各位提供一个参考,不一定就都是这个问题导致的。 最近升级…

    Java 2023年6月13日
    0181
  • SQL Server2008安装详细教程

    链接 posted @2022-07-05 22:17 叫我小锅锅 阅读(64 ) 评论() 编辑 Original: https://www.cnblogs.com/lhboke…

    Java 2023年6月5日
    065
  • AOP

    AOP AOP的入门案例: AOP的工作流程 SpringAop的本质是:代理模式 AOP的切入点表达式 重用切入点表达式: ①声明 @Pointcut(“execut…

    Java 2023年6月16日
    058
  • Java WebService 简单实例

    前言:朋友们开始以下教程前,请先看第五大点的注意事项,以避免不必要的重复操作。 一、准备工作(以下为本实例使用工具) 1、MyEclipse10.7.1 2、JDK 1.6.0_2…

    Java 2023年5月29日
    0133
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球