Java设计模式再相识 (十七)——状态模式

目录

  • 状态模式
    • 状态模式的定义
    • 状态模式的结构
    • 状态模式的具体应用场景
    • 示例
    • 状态模式的优缺点
      • 优点
      • 缺点
  • 总结

在软件开发中,我们通常会为对象定义状态来适应不同的场景。在传统程序中,我们通常会定义状态变量来进行不同状态的切换,再通过if else语句来进行逻辑的判断,从而使不同状态的执行行为发生改变。但这样做会造成软件的臃肿和难以维护。在引入新的状态时,需要增加新的条件判断语句,这违背了“开闭原则”。本文将使用设计模式之状态模式来解决这个问题。

状态模式

状态模式的定义

状态模式(State):对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

在状态模式中,我们不将“状态”简单地定义为变量来进行处理,而是将其定义为类,用类来表示状态。这可能是个抽象的概念,下文我们将使用实例来进行详细讲解。

状态模式的结构

  • 上下文/环境(Context):它维护了一个当前的状态,并提供了供开发者调用的接口,负责具体状态的切换。
  • 抽象状态(State):接口,定义了上下文对象中状态对应的方法。
  • 具体状态(ConcreteState):实现抽象状态接口中定义的所有方法。

状态模式的具体应用场景

  • 聊天软件中的不同状态:如在线、离线、忙碌状态
  • 状态机:操作系统往往需要管理不同的状态。例如安卓操作系统中就有基于状态树的决策机制。具体实现过程不是本篇的重点,感兴趣可以自行查阅操作系统的资料。

示例

本文,我们来实现一个商城订单的四种不同状态:分别是待付款,待发货,待收货和退款中状态。

不同状态有着不同的处理逻辑。

  • 待付款状态下,系统会提示用户尽快付款。
  • 待发货状态下,系统会提示商家尽快发货。
  • 待收获状态下,系统会为用户推送物流信息。

我们使用状态模式处理这个需求。

下面我们开始编写具体代码。

首先我们先来定义一个抽象状态(State):

State.java

package com.yeliheng.state;

/**
 * 抽象状态
 */
public abstract class State {
    public abstract void handle(Context context);
}

在抽象状态类中,我们定义了一个抽象方法handle() 用于处理传入的上下文对象。

接着,我们可以定义一个Context类。

Context.java

package com.yeliheng.state;

public class Context {

    private State state;

    public Context() {
        this.state = new PendingPaymentState();
    }

    public State getState() {
        return state;
    }

    public void setState(State state) {
        this.state = state;
    }

    public void handle() {
        state.handle(this);
    }
}

上下文对象中,我们定义了一个状态变量State,并提供相应的setter getter方法。方便后续具体状态设置即将切换的下一个状态。并且我们初始化初状态为PendingPaymentState即待付款状态。

我们开始定义具体的状态类。

PendingPaymentState.java

package com.yeliheng.state;

public class PendingPaymentState extends State{
    @Override
    public void handle(Context context) {
        System.out.println("[订单待付款-买家中心] 您有一笔订单等待付款。");
        context.setState(new PendingDeliveryState());
    }
}

在待付款状态中,系统会输出用户提示,并将下一个状态指定给待发货状态。代码实现很简单,另外两个具体状态类同理。

PendingReceiptState.java


package com.yeliheng.state;

public class PendingReceiptState extends State{
    @Override
    public void handle(Context context) {
        System.out.println("[订单待收货-买家中心] 卖家已发货,当前物流在:XXX省XXX市");
    }
}

PendingDeliveryState.java

package com.yeliheng.state;

public class PendingDeliveryState extends State{
    @Override
    public void handle(Context context) {
        System.out.println("[订单待发货-卖家中心] 买家已付款,请尽快发货。");
        context.setState(new PendingReceiptState());
    }
}

最后我们可以在main函数中调用此程序,观察输出结果。

Main.java

package com.yeliheng.state;

public class Main {
    public static void main(String[] args) {
        Context context = new Context(); //注册上下文
        //买家付款
        context.handle();
        //卖家发货
        context.handle();
        //买家收货
        context.handle();

    }
}

在Main类中,我们初始化了一个上下文类,并调用了三次handle方法,分别模拟了三种订单状态。

最终程序的输出结果如下图所示:

output.png

在状态模式的应用中,我们可以发现,状态变量被不同的类代替了。并且,原先臃肿的if else逻辑被状态所替代,统一交由上下文(Context)类进行管理,程序变得更加具有条理性,这有助于项目的拓展。

状态模式的优缺点

优点

  • 将状态进行封装有利于减少对象间的相互依赖。
  • 状态类的职责明确,符合类的“单一职责”原则。
  • 状态模式结构清晰,有利于增加项目的可拓展性,降低类之间的耦合。

缺点

  • 过度使用状态模式会造成状态类的大规模增长,提升系统的维护难度。
  • 状态模式有背开闭原则。增加新的状态,必须将管理状态的上下文类进行修改才能达到目的。

总结

使用状态模式能够有效减少复杂的判断逻辑,增加程序的可维护性。本例通过商城中订单状态的切换来详解状态模式。

本例的完整源代码参见:Github

Java设计模式行为型模式
2025 © Yeliheng的技术小站 版权所有