23种设计模式(七)-状态设计模式 (上)

简介: 23种设计模式(七)-状态设计模式

一. 什么是状态模式?



状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换。当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类。例如:淘宝下单,订单有待付款,已付款待发货,待收货,待评价, 已完成等状态。每个状态对应的行为都是不同的, 一个状态完成会流转到下一个状态。


通常对有状态的对象进行编程,我们的解决方案是:思考可能存在的所有状态,然后使用 if-else 或 switch-case 语句来进行状态判断,然后再根据不同的状态进行不同的处理。如上面的案例--淘宝下单:


public class ClientApplication {
    public static void main(String[] args) {
        String status = "待付款";
        if ("待付款".equals(status)) {
            // 执行付款逻辑
        } else if("待发货".equals(status)) {
            // 执行发货逻辑
        } else if("待收货".equals(status)) {
            // 执行收货逻辑
        } else if("待评价".equals(status)) {
            // 执行评价逻辑
        } else if("待收货".equals(status)) {
            // 执行收获逻辑
        }
    }
}

大量的if...else的缺点很明显


  1. 违背开闭原则: 当增加一种状态的时候, 需要修改原来的逻辑
  2. 当状态很多的时候, 代码段很长, 臃肿, 不容易维护, 可扩展性差.

状态模式可以很好地解决这个问题。


状态模式的思想:当是条件语句表示一个对象状态转换过于复杂时,可以把条件判断中的“判断逻辑”提取出来,放在单独的类中,当前上下文处于那种状态,直接用相应的

状态类对象进行处理,这样的好处是:能把原来复杂的判断逻辑简单化,消除了 if-else、switch-case 等条件判断语句,代码更有层次性,且具备良好的扩展力, 可维护性。


二. 状态模式的结构



状态模式把因环境改变而变化的对象行为包装在不同的状态类中,其目的保证对象的状态变化时, 其行为也随之改变。接下来, 我们来看看状态模式的结构。

24d51dfd702046ec92c4f927288a847b_tplv-k3u1fbpfcp-zoom-1.png

从中我们可以看出有四个组成部分:


1. 环境类


环境类也是上下文类, 状态的变化依赖于环境, 环境记录当前的状态. 环境类拥有多种状态的对象. 环境类的状态存在多样性, 不同状态下对象的行为有所不同, 因此将状态独立出去形成单独的状态类。


> 1) 在环境类中维护一个抽象状态State的实例, 这个实例存储当前状态。
> 2) 在环境类中定义所有状态执行的方法.
package com.lxl.www.designPatterns.statePattern.context;
/**
 * 上下文环境类
 */
public class Context {
    // 在环境类中维护一个抽象状态State的实例, 这个实例存储当前状态。
    private State state;
    public Context() {
        this.state = new ConcreteStateA(this);
    }
    public void setState(State state) {
        this.state = state;
    }
    /**
     * 在环境类中定义所有状态执行的方法.
     */
    public void handler1() {
        this.state.handler1();
    }
    public void handler2() {
        this.state.handler2();
    }
}

2. State抽象状态类


  抽象环境中声明一个环境角色,提供各个状态类自行访问,并且提供所有状态的抽象行为,由各个实现类实现。

/**
 * 状态抽象类
 */
public abstract class State {
    /**
     * 环境上下文
     */
    public Context context;
    public State(Context context) {
        this.context = context;
    }
    /**
     * 定义了所有状态的抽象方法
     */
    abstract void handler1();
    abstract void handler2();
}

3. 具体状态


  具体状态实现,这里以定义ConcreteStateA、ConcreteStateB、ConcreteStateC三个具体状态类。具体状态类继承自State抽象类, 并实现抽象方法。


package com.lxl.www.designPatterns.statePattern.context;
public class ConcreteStateA extends State{
    public ConcreteStateA(Context context) {
        super(context);
    }
    @Override
    void handler1() {
        System.out.println("执行ConcreteStateA中handler1的逻辑");
        this.context.setState(new ConcreteStateB(context));
    }
    @Override
    void handler2() {
        System.out.println("执行ConcreteStateA中handler2的逻辑");
        this.context.setState(new ConcreteStateC(context));
    }
}

在这里, 我们重写了状态类的状态方法。当执行完方法一的业务逻辑后, 将状态变更为 ConcreteStateB. 执行完handler2的业务逻辑后, 将状态变更为ConcreteStateC。

这种自动变更状态, 在调用方是无感知的。


4. Client客户端


  构建Context环境上下文类的实例对象,初始化时设置初始状态是ConcreteStateA,执行行为观察结果。


package com.lxl.www.designPatterns.statePattern.context;
public class Client {
    public static void main(String[] args) {
        Context context = new Context();
        context.handler1();
        context.handler1();
        context.handler1();
        context.handler2();
    }
}

全部源码:

package com.lxl.www.designPatterns.statePattern.context;
/**
 * 上下文环境类
 */
public class Context {
    // 在环境类中维护一个抽象状态State的实例, 这个实例存储当前状态。
    private State state;
    public Context() {
        this.state = new ConcreteStateA(this);
    }
    public void setState(State state) {
        this.state = state;
    }
    /**
     * 在环境类中定义所有状态执行的方法.
     */
    public void handler1() {
        this.state.handler1();
    }
    public void handler2() {
        this.state.handler2();
    }
}
/**
 * 状态抽象类
 */
public abstract class State {
    /**
     * 环境上下文
     */
    public Context context;
    public State(Context context) {
        this.context = context;
    }
    /**
     * 定义了所有状态的抽象方法
     */
    abstract void handler1();
    abstract void handler2();
}
public class ConcreteStateA extends State{
    public ConcreteStateA(Context context) {
        super(context);
    }
    @Override
    void handler1() {
        System.out.println("执行ConcreteStateA中handler1的逻辑");
        this.context.setState(new ConcreteStateB(context));
    }
    @Override
    void handler2() {
        System.out.println("执行ConcreteStateA中handler2的逻辑");
        this.context.setState(new ConcreteStateC(context));
    }
}
public class ConcreteStateB extends State{
    public ConcreteStateB(Context context) {
        super(context);
    }
    @Override
    void handler1() {
        System.out.println("执行ConcreteStateB中handler1的逻辑");
        this.context.setState(new ConcreteStateC(context));
    }
    @Override
    void handler2() {
        System.out.println("执行ConcreteStateB中handler2的逻辑");
        this.context.setState(new ConcreteStateA(context));
    }
}
public class ConcreteStateC extends State{
    public ConcreteStateC(Context context) {
        super(context);
    }
    @Override
    void handler1() {
        System.out.println("执行ConcreteStateC中handler1的逻辑");
        this.context.setState(new ConcreteStateA(context));
    }
    @Override
    void handler2() {
        System.out.println("执行ConcreteStateC中handler2的逻辑");
        this.context.setState(new ConcreteStateB(context));
    }
}
public class Client {
    public static void main(String[] args) {
        Context context = new Context();
        context.handler1();
        context.handler1();
        context.handler1();
        context.handler1();
    }
}

客户端直接结果分析:


  1. Context实例花的时候, 状态是 ConcreteStateA
  2. 第一次执行context.handler1();将状态切换到了ConcreteStateB
  3. 第二次执行context.handler1();将状态切换到了ConcreteStateC
  4. 第三次执行context.handler1();将状态切换到了ConcreteStateA


所以, 最后的运行结果是:


执行ConcreteStateA中handler1的逻辑


执行ConcreteStateB中handler1的逻辑


执行ConcreteStateC中handler1的逻辑


执行ConcreteStateA中handler1的逻辑


三. 状态模式的应用实例


下面以商品下单为例,来看看状态模式的应用。

在电商系统购物的过程中,订单一般有这几种状态,待付款,待出库,待收货, 待评价,完成。不同的状态下,用户看到的行为是不同的。

我们由浅入深, 来看看几种状态模式的实现。


第一种: 状态是一条线的实现逻辑


先来看看这种方法的流程:

27f97c621480425c9985c919d66f4233_tplv-k3u1fbpfcp-zoom-1.png

这种状态的特点是: 上一个状态流转到下一个状态, 下一个流转到下下个,以此类推。直到最后一个没有状态流转了。来看看源码:


第一步:定义上下文环境类。


用orderStatus来记录当前的状态。并定义了所有状态的方法。


package com.lxl.www.designPatterns.statePattern.order;
/**
 * 订单上下文
 */
public class OrderContext {
    /**在上下文类中记录订单状态*/
    private IOrderStatus orderStatus;
    public OrderContext() {
        // 最开始是待付款状态
        this.orderStatus = new Pending();
    }
    public void setOrderStatus(IOrderStatus orderStatus) {
        this.orderStatus = orderStatus;
    }
    /**
     * 业务逻辑操作
     */
    public void businessHandler() {
        this.orderStatus.businessHandler(this);
    }
    /**
     * 打印当前业务
     */
    public void printInfo() {
        this.orderStatus.printInfo();
    }
}

第二步: 定义状态抽象类。


里面定义所有状态类要实现的方法。


package com.lxl.www.designPatterns.statePattern.order;
/**
 * 订单状态
 */
public interface IOrderStatus {
    void businessHandler(OrderContext context);
    void printInfo();
}

这里面就有两个方法, 一个是状态要执行的业务逻辑, 另一个是打印当前状态。


第三步:定义状态具体类。


这里定义了5个具体类,分别是:待付款,待出库,待收货, 待评价,完成

package com.lxl.www.designPatterns.statePattern.order;
/**
 * 待付款
 */
public class Pending implements IOrderStatus{
    @Override
    public void businessHandler(OrderContext context) {
        //执行业务, 完成付款, 进入到下一个状态
        System.out.println("付款完成");
        context.setOrderStatus(new WaitOut());
    }
    @Override
    public void printInfo() {
        System.out.println("当前状态等::待付款");
    }
}
package com.lxl.www.designPatterns.statePattern.order;
/**
 * 待出库
 */
public class WaitOut implements IOrderStatus{
    @Override
    public void businessHandler(OrderContext context) {
        //执行业务, 完成付款, 进入到下一个状态
        System.out.println("货物已出库");
        context.setOrderStatus(new WaitReceive());
    }
    @Override
    public void printInfo() {
        System.out.println("当前状态等::待出库");
    }
}
package com.lxl.www.designPatterns.statePattern.order;
/**
 * 待收货
 */
public class WaitReceive implements IOrderStatus {
    @Override
    public void businessHandler(OrderContext context) {
        //执行业务, 完成付款, 进入到下一个状态
        System.out.println("已经收货");
        context.setOrderStatus(new OrderEvaluation());
    }
    @Override
    public void printInfo() {
        System.out.println("当前状态等::待收货");
    }
}
package com.lxl.www.designPatterns.statePattern.order;
/**
 * 订单评价
 */
public class OrderEvaluation implements IOrderStatus{
    @Override
    public void businessHandler(OrderContext context) {
        //执行业务, 完成付款, 进入到下一个状态
        System.out.println("已经评价");
        context.setOrderStatus(new Finish());
    }
    @Override
    public void printInfo() {
        System.out.println("当前状态等::待评价");
    }
}
package com.lxl.www.designPatterns.statePattern.order;
/**
 * 订单完成
 */
public class Finish implements IOrderStatus{
    @Override
    public void businessHandler(OrderContext context) {
        // 执行业务, 完成付款, 进入到下一个状态
        System.out.println("完成工作处理完毕");
    }
    @Override
    public void printInfo() {
        System.out.println("当前状态::订单完成");
    }
}

第四步: 定义客户端类, 模拟下单的流程


package com.lxl.www.designPatterns.statePattern.order;
public class OrderClient {
    public static void main(String[] args) {
        OrderContext orderContext = new OrderContext();
        // 开始下单
        System.out.println("==========开始下单==========");
        orderContext.printInfo();
        // 付款
        System.out.println("==========付款==========");
        orderContext.businessHandler();
        orderContext.printInfo();
        // 货物出库
        System.out.println("==========货物出库==========");
        orderContext.businessHandler();
        orderContext.printInfo();
        // 收货
        System.out.println("==========收货==========");
        orderContext.businessHandler();
        orderContext.printInfo();
        // 评价订单
        System.out.println("==========评价订单==========");
        orderContext.businessHandler();
        orderContext.printInfo();
        // 订单完成
        System.out.println("==========订单完成==========");
        orderContext.businessHandler();
    }
}

接下来我们来看看运行效果:

开始下单


当前状态等::待付款


付款


付款完成


当前状态等::待出库


货物出库


货物已出库


当前状态等::待收货


收货


已经收货


当前状态等::待评价


评价订单


已经评价


当前状态::订单完成


订单完成


完成工作处理完毕

相关文章
|
存储 缓存 容灾
AIGC 商业化道路探索 - Stable Diffusion 商业化应用(下)
Stable Diffusion 应用到商业领域的案例越来越多,商用场景下的技术架构应当如何构建?本文基于阿里云近期的一个 Stable Diffusion 商业案例,对大规模底模切换、大量 LoRA 调优的场景提出一个商业场景适用的技术架构,并已实现部署交付,稳定运行。
|
存储 数据库 索引
Python新手常见问题一:列表、元组、集合、字典区别是什么?
本文针对Python编程新手常遇到的问题,详细阐述了列表(List)、元组(Tuple)、集合(Set)和字典(Dictionary)这四种数据结构的核心区别。列表是一种有序且可变的数据序列,允许元素重复;元组同样有序但不可变,其内容一旦创建就不能修改;集合是无序、不重复的元素集,强调唯一性,主要用于数学意义上的集合操作;而字典则是键值对的映射容器,其中键必须唯一,而值可以任意,它提供了一种通过键查找对应值的有效方式。通过对这些基本概念和特性的对比讲解,旨在帮助初学者更好地理解并运用这些数据类型来解决实际编程问题。
5095 1
|
6月前
|
人工智能 运维 安全
企业如何降低 Claude 的迁移与接入门槛
企业AI落地面临多模型并行、接口杂乱、跨境支付与合规难、治理缺失等痛点。通过统一OpenAI标准接口、接入聚合平台及LLM网关,可实现模型热切换、人民币结算、网络加速与精细化管控,降本增效又保障稳定安全。
247 0
|
8月前
|
存储 弹性计算 安全
阿里云无影云电脑解读:优势、功能、收费价格及使用场景全解析
阿里云无影云电脑提供安全、灵活、高效的云上办公解决方案,支持多端接入、弹性配置与数据防泄密,适用于企业协作、专业设计及家庭共享等场景,助力数字化转型。
|
10月前
|
SQL 存储 关系型数据库
MySQL中到底什么是覆盖索引、索引下推?
覆盖索引指查询只需通过索引即可获取数据,无需回表,提升查询效率。索引下推则在索引遍历时提前过滤条件,减少回表次数,尤其适用于联合索引中部分字段无法使用的情况,二者均能显著降低I/O开销,提高查询性能。(238字)
896 1
|
10月前
|
JSON 前端开发 文件存储
开源项目,全网音乐免费听,太牛逼啦,XiaoMusic 无限点歌机~~~~
XiaoMusic 是一款开源的小爱音箱音乐增强工具,支持全网音乐免费听。通过 NAS 或电脑部署,结合 yt-dlp 下载与本地音乐管理,实现语音点歌、搜索播放、多设备控制,让小爱变身家庭音乐中枢。
3652 2
|
人工智能 自然语言处理 安全
中文竞技场-中文大模型比比看
今天,大语言模型正在各个应用领域引起巨大的变革,并已经在搜索、金融、办公、安全、教育、游戏、电商、社交媒体等领域迅速普及和应用。例如微软将 GPT4应用于必应搜索引擎和 Office 办公软件。几乎每个企业都试图探索如何将AI融入业务和技术中去。但以中文为主的语言大模型却缺少应有的关注,今天让我们聚焦中文竞技场,看看各种中文大语言模型的表现吧~
64593 1
中文竞技场-中文大模型比比看
|
存储 缓存 前端开发
软件架构分层,你的项目处于什么阶段?
软件架构分层,你的项目处于什么阶段?
2156 0
软件架构分层,你的项目处于什么阶段?