Java状态模式:解构行为随状态变化的优雅设计与实现策略

为什么有状态模式?

在软件开发中,有些对象的行为取决于其内部状态,而且这些状态可能在运行时发生变化。如果直接在对象内部使用大量的条件语句来管理状态转换,会导致代码变得复杂、难以维护。状态模式就是为了解决这一问题而诞生的。

在Java中,状态模式通常包含以下角色:

  1. 环境类(Context):
    定义了客户端需要的接口,并且维护了一个指向当前状态的引用。
    负责调用当前状态下对应的方法,并根据业务需求切换状态对象。
  2. 抽象状态(State):
    是所有具体状态类的基类或者接口,定义了所有可能的状态共有的方法以及与环境类交互的接口。
  3. 具体状态(Concrete State):
    每个具体状态类实现抽象状态定义的行为,这些类代表对象的不同状态,并可以决定何时以及如何进行状态转换。

代码示例

在一个ATM机系统中,环境类可能是ATM机器本身,而状态类可以是“取款中”、“存款中”、“查询余额中”等具体状态。每种状态下ATM的行为不同,如“取款中”状态下可以执行取款操作和退卡操作,而在“查询余额中”状态下只能查询余额。

// 抽象状态
abstract class State {
    protected ATM atm; // ATM环境

    public void setATM(ATM atm) {
        this.atm = atm;
    }

    // 抽象方法,由具体状态来实现
    public abstract void insertCard();
    public abstract void ejectCard();
    public abstract void withdrawMoney(int amount);
}

具体状态:

// 具体状态
class NoCardState extends State {
    @Override
    public void insertCard() {
        System.out.println("卡片已插入,状态变为有卡状态");
        atm.setCurrentState(atm.getHasCardState()); // 状态切换
    }

    // ... 实现其他方法
}

// 其他具体状态...
class HasCardState extends State {
    @Override
    public void ejectCard() {
        System.out.println("卡片已退出,状态变为无卡状态");
        atm.setCurrentState(atm.getNoCardState()); // 状态切换
    }

    @Override
    public void withdrawMoney(int amount) {
        if (amount <= atm.getBalance()) {
            atm.setBalance(atm.getBalance() - amount);
            System.out.println("取款成功,金额为: " + amount);
        } else {
            System.out.println("余额不足,无法取款");
        }
    }

    // ... 其他方法实现
}

ATM环境类(上下文):

class ATM {
    private int balance;
    private State currentState;

    public ATM() {
        currentState = new NoCardState(); // 初始化状态
        currentState.setATM(this);
    }

    // 获取不同状态实例
    private NoCardState getNoCardState() { /*...*/ }
    private HasCardState getHasCardState() { /*...*/ }

    // 状态变更方法
    public void setCurrentState(State state) {
        currentState = state;
        currentState.setATM(this);
    }

    // ... 其他相关方法,如获取

jdk中的状态模式

状态模式允许对象在其内部状态改变时改变其行为,使得对象看起来像是改变了它的类。

  1. java.util.Iterator 接口以及其实现类在遍历集合元素时就体现了一种状态变化的过程。iterator的不同方法(如hasNext()、next()、remove()等)对应了迭代器的不同状态,并根据当前状态决定行为。
  2. javax.faces.lifecycle.LifeCycle#execute() 在JSF(JavaServer Faces)框架中,生命周期管理就是一个典型的状态机应用,每个阶段(Restore View、Apply Request Values、Process Validations 等)代表一个状态,当执行到不同的阶段时,行为会有所不同。
  3. Swing/AWT GUI Toolkit中的组件状态 诸如按钮、菜单等GUI组件,在处理鼠标点击、键盘输入等事件时,其内部可能会使用状态模式来维护不同状态下的表现和行为。

具体业务代码实现分析

在使用状态模式实现订单状态管理时,可以为每个订单状态定义一个独立的状态类。例如:

// 抽象状态接口或抽象类
public abstract class OrderState {
    protected Order order;

    public void setOrder(Order order) {
        this.order = order;
    }

    // 定义每个状态下通用的方法,如获取状态名等
    public abstract String getStateName();

    // 状态转换方法,由具体状态类实现
    public abstract void pay();
}

具体状态类 - 待支付状态:


public class PendingPaymentState extends OrderState {
    @Override
    public String getStateName() {
        return "待支付";
    }

    @Override
    public void pay() {
        // 检查支付条件,处理支付逻辑(调用第三方支付API等)
        // 支付成功后切换状态
        if (paymentSuccessful()) {
            order.setState(new PaidState());
            // 更新数据库或其他操作...
        }
    }

    private boolean paymentSuccessful() {
        // 实现支付成功的验证逻辑
        // ...
        return true; // 假设支付成功
    }
}

具体状态类 - 已支付状态:


public class PaidState extends OrderState {
    @Override
    public String getStateName() {
        return "已支付";
    }

    @Override
    public void pay() {
        // 在已支付状态下再次支付通常不需要处理,或者抛出异常表示状态错误
        throw new IllegalStateException("订单已支付,无法再次支付");
    }
}

订单实体类:

public class Order {
    private OrderState state;

    // 构造函数初始化状态
    public Order() {
        this.state = new PendingPaymentState();
        state.setOrder(this);
    }

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

    public void pay() {
        // 调用当前状态下的支付行为
        state.pay();
    }

    // 其他与订单相关的业务方法...
}

当调用order.pay()方法时,实际执行的操作会根据当前订单状态的不同而不同。当订单从“待支付”状态变为“已支付”状态时,其行为规则也随之改变。

状态模式优缺点

优点:

  1. 职责分离:将不同状态下的行为划分到不同的类中,遵循了“单一职责原则”,让代码结构更清晰,易于理解和维护。
  2. 扩展性好:新的状态可以通过添加新的状态类轻松实现,而无需修改原有代码。
  3. 减少条件判断:将状态相关的逻辑分散到各个状态类中,可以避免在上下文对象中使用复杂的条件分支语句来决定行为。
  4. 封装状态转换规则:状态之间的转换逻辑被封装在状态类中,降低了对象间的耦合度,使状态变化更为透明和明确。
  5. 易于理解与调试:每个状态类专注于自身的处理逻辑,便于测试和调试。
    缺点:
  6. 增加类的数量:随着系统状态数量的增长,状态类的数量也会相应增加,可能导致类爆炸问题,从而增加了系统的复杂性和管理难度。
  7. 状态转换复杂时难以维护:如果状态转换关系非常复杂或者有大量状态之间相互转换的情况,可能会导致状态类之间的耦合度增加,使得状态模式变得难以维护。
  8. 上下文需要维护状态对象:上下文对象必须持有当前状态对象的引用,并负责状态之间的切换,这会增加上下文类的复杂性。
  • 36
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

π克

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值