MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java状态模式的应用示例

2024-03-284.2k 阅读

Java 状态模式的概念与原理

状态模式的定义

状态模式(State Pattern)是一种行为型设计模式,它允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。这种模式将状态封装成独立的类,并将请求委托给当前的状态对象,使得对象的行为可以根据其状态动态地改变。

状态模式的结构

  1. 环境(Context):持有一个状态对象的引用,定义客户端感兴趣的接口,并将与状态相关的操作委托给当前的状态对象处理。
  2. 抽象状态(State):定义一个接口,封装与环境的一个特定状态相关的行为。
  3. 具体状态(ConcreteState):实现抽象状态定义的接口,每个具体状态类对应环境的一个状态,实现与该状态相关的行为。

状态模式的优势

  1. 可维护性:将不同状态的行为封装在不同的类中,当需要修改某个状态的行为时,只需要修改对应的具体状态类,不会影响其他状态类。
  2. 可扩展性:增加新的状态非常容易,只需要创建一个新的具体状态类并实现抽象状态接口即可。
  3. 清晰的状态管理:状态模式使得状态转换和状态相关的行为变得清晰明了,提高了代码的可读性。

Java 状态模式的简单示例

场景描述

假设我们有一个自动售货机,它有不同的状态,如“有商品”、“无商品”、“投币”、“出货”等。当售货机处于不同状态时,对投币、退币、出货等操作的响应是不同的。

代码实现

  1. 抽象状态类
abstract class VendingMachineState {
    protected VendingMachine vendingMachine;

    public VendingMachineState(VendingMachine vendingMachine) {
        this.vendingMachine = vendingMachine;
    }

    public abstract void insertCoin();
    public abstract void ejectCoin();
    public abstract void dispense();
}
  1. 具体状态类 - 有商品状态
class HasProductState extends VendingMachineState {
    public HasProductState(VendingMachine vendingMachine) {
        super(vendingMachine);
    }

    @Override
    public void insertCoin() {
        System.out.println("已经投币,等待出货。");
        vendingMachine.setState(vendingMachine.getDispensingState());
    }

    @Override
    public void ejectCoin() {
        System.out.println("退币成功。");
        vendingMachine.setState(vendingMachine.getNoCoinState());
    }

    @Override
    public void dispense() {
        System.out.println("请先投币。");
    }
}
  1. 具体状态类 - 无商品状态
class NoProductState extends VendingMachineState {
    public NoProductState(VendingMachine vendingMachine) {
        super(vendingMachine);
    }

    @Override
    public void insertCoin() {
        System.out.println("无商品,投币无效。");
    }

    @Override
    public void ejectCoin() {
        System.out.println("无商品,无法退币。");
    }

    @Override
    public void dispense() {
        System.out.println("无商品,无法出货。");
    }
}
  1. 具体状态类 - 投币状态
class HasCoinState extends VendingMachineState {
    public HasCoinState(VendingMachine vendingMachine) {
        super(vendingMachine);
    }

    @Override
    public void insertCoin() {
        System.out.println("已经投币,无需重复投币。");
    }

    @Override
    public void ejectCoin() {
        System.out.println("退币成功。");
        vendingMachine.setState(vendingMachine.getNoCoinState());
    }

    @Override
    public void dispense() {
        System.out.println("出货成功。");
        vendingMachine.setState(vendingMachine.getNoCoinState());
        if (vendingMachine.getProductCount() > 0) {
            vendingMachine.setProductCount(vendingMachine.getProductCount() - 1);
            if (vendingMachine.getProductCount() == 0) {
                vendingMachine.setState(vendingMachine.getNoProductState());
            }
        }
    }
}
  1. 具体状态类 - 出货状态
class DispensingState extends VendingMachineState {
    public DispensingState(VendingMachine vendingMachine) {
        super(vendingMachine);
    }

    @Override
    public void insertCoin() {
        System.out.println("正在出货,请勿投币。");
    }

    @Override
    public void ejectCoin() {
        System.out.println("正在出货,无法退币。");
    }

    @Override
    public void dispense() {
        System.out.println("出货中...");
        vendingMachine.setState(vendingMachine.getHasCoinState());
    }
}
  1. 环境类 - 自动售货机
class VendingMachine {
    private int productCount;
    private VendingMachineState noCoinState;
    private VendingMachineState hasCoinState;
    private VendingMachineState noProductState;
    private VendingMachineState dispensingState;
    private VendingMachineState currentState;

    public VendingMachine(int productCount) {
        this.productCount = productCount;
        noCoinState = new NoCoinState(this);
        hasCoinState = new HasCoinState(this);
        noProductState = new NoProductState(this);
        dispensingState = new DispensingState(this);

        if (productCount > 0) {
            currentState = noCoinState;
        } else {
            currentState = noProductState;
        }
    }

    public void insertCoin() {
        currentState.insertCoin();
    }

    public void ejectCoin() {
        currentState.ejectCoin();
    }

    public void dispense() {
        currentState.dispense();
    }

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

    public VendingMachineState getNoCoinState() {
        return noCoinState;
    }

    public VendingMachineState getHasCoinState() {
        return hasCoinState;
    }

    public VendingMachineState getNoProductState() {
        return noProductState;
    }

    public VendingMachineState getDispensingState() {
        return dispensingState;
    }

    public int getProductCount() {
        return productCount;
    }

    public void setProductCount(int productCount) {
        this.productCount = productCount;
    }
}
  1. 测试类
public class VendingMachineTest {
    public static void main(String[] args) {
        VendingMachine vendingMachine = new VendingMachine(1);
        vendingMachine.insertCoin();
        vendingMachine.dispense();
        vendingMachine.ejectCoin();
        vendingMachine.insertCoin();
        vendingMachine.dispense();
    }
}

在实际项目中的应用

工作流系统

在工作流系统中,一个任务可能有不同的状态,如“新建”、“待审批”、“审批通过”、“审批拒绝”等。不同状态下,任务对操作(如提交审批、重新提交、关闭任务等)的响应不同。

  1. 抽象状态类
abstract class TaskState {
    protected Task task;

    public TaskState(Task task) {
        this.task = task;
    }

    public abstract void submitForApproval();
    public abstract void resubmit();
    public abstract void closeTask();
}
  1. 具体状态类 - 新建状态
class NewTaskState extends TaskState {
    public NewTaskState(Task task) {
        super(task);
    }

    @Override
    public void submitForApproval() {
        System.out.println("任务已提交审批。");
        task.setState(task.getPendingApprovalState());
    }

    @Override
    public void resubmit() {
        System.out.println("任务尚未提交,无需重新提交。");
    }

    @Override
    public void closeTask() {
        System.out.println("任务已关闭。");
        task.setState(task.getClosedTaskState());
    }
}
  1. 具体状态类 - 待审批状态
class PendingApprovalState extends TaskState {
    public PendingApprovalState(Task task) {
        super(task);
    }

    @Override
    public void submitForApproval() {
        System.out.println("任务已在审批中,无需再次提交。");
    }

    @Override
    public void resubmit() {
        System.out.println("请先等待审批结果。");
    }

    @Override
    public void closeTask() {
        System.out.println("无法关闭待审批任务。");
    }
}
  1. 具体状态类 - 审批通过状态
class ApprovedTaskState extends TaskState {
    public ApprovedTaskState(Task task) {
        super(task);
    }

    @Override
    public void submitForApproval() {
        System.out.println("任务已通过审批,无需再次提交。");
    }

    @Override
    public void resubmit() {
        System.out.println("任务已通过审批,无需重新提交。");
    }

    @Override
    public void closeTask() {
        System.out.println("任务已关闭。");
        task.setState(task.getClosedTaskState());
    }
}
  1. 具体状态类 - 审批拒绝状态
class RejectedTaskState extends TaskState {
    public RejectedTaskState(Task task) {
        super(task);
    }

    @Override
    public void submitForApproval() {
        System.out.println("请先修改任务后重新提交。");
    }

    @Override
    public void resubmit() {
        System.out.println("任务重新提交。");
        task.setState(task.getPendingApprovalState());
    }

    @Override
    public void closeTask() {
        System.out.println("任务已关闭。");
        task.setState(task.getClosedTaskState());
    }
}
  1. 具体状态类 - 关闭状态
class ClosedTaskState extends TaskState {
    public ClosedTaskState(Task task) {
        super(task);
    }

    @Override
    public void submitForApproval() {
        System.out.println("已关闭任务,无法提交审批。");
    }

    @Override
    public void resubmit() {
        System.out.println("已关闭任务,无法重新提交。");
    }

    @Override
    public void closeTask() {
        System.out.println("任务已关闭。");
    }
}
  1. 环境类 - 任务
class Task {
    private TaskState newTaskState;
    private TaskState pendingApprovalState;
    private TaskState approvedTaskState;
    private TaskState rejectedTaskState;
    private TaskState closedTaskState;
    private TaskState currentState;

    public Task() {
        newTaskState = new NewTaskState(this);
        pendingApprovalState = new PendingApprovalState(this);
        approvedTaskState = new ApprovedTaskState(this);
        rejectedTaskState = new RejectedTaskState(this);
        closedTaskState = new ClosedTaskState(this);
        currentState = newTaskState;
    }

    public void submitForApproval() {
        currentState.submitForApproval();
    }

    public void resubmit() {
        currentState.resubmit();
    }

    public void closeTask() {
        currentState.closeTask();
    }

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

    public TaskState getNewTaskState() {
        return newTaskState;
    }

    public TaskState getPendingApprovalState() {
        return pendingApprovalState;
    }

    public TaskState getApprovedTaskState() {
        return approvedTaskState;
    }

    public TaskState getRejectedTaskState() {
        return rejectedTaskState;
    }

    public TaskState getClosedTaskState() {
        return closedTaskState;
    }
}
  1. 测试类
public class TaskTest {
    public static void main(String[] args) {
        Task task = new Task();
        task.submitForApproval();
        task.resubmit();
        task.closeTask();
    }
}

游戏角色状态管理

在游戏开发中,游戏角色可能有不同的状态,如“站立”、“行走”、“跳跃”、“攻击”等。不同状态下,角色对输入(如按键操作)的响应不同。

  1. 抽象状态类
abstract class CharacterState {
    protected GameCharacter character;

    public CharacterState(GameCharacter character) {
        this.character = character;
    }

    public abstract void handleInput(String input);
}
  1. 具体状态类 - 站立状态
class StandingState extends CharacterState {
    public StandingState(GameCharacter character) {
        super(character);
    }

    @Override
    public void handleInput(String input) {
        if ("WALK".equals(input)) {
            System.out.println("角色开始行走。");
            character.setState(character.getWalkingState());
        } else if ("JUMP".equals(input)) {
            System.out.println("角色跳跃。");
            character.setState(character.getJumpingState());
        } else if ("ATTACK".equals(input)) {
            System.out.println("角色攻击。");
            character.setState(character.getAttackingState());
        }
    }
}
  1. 具体状态类 - 行走状态
class WalkingState extends CharacterState {
    public WalkingState(GameCharacter character) {
        super(character);
    }

    @Override
    public void handleInput(String input) {
        if ("STOP".equals(input)) {
            System.out.println("角色停止行走,进入站立状态。");
            character.setState(character.getStandingState());
        } else if ("JUMP".equals(input)) {
            System.out.println("角色边行走边跳跃。");
            character.setState(character.getJumpingState());
        } else if ("ATTACK".equals(input)) {
            System.out.println("角色在行走中攻击。");
            character.setState(character.getAttackingState());
        }
    }
}
  1. 具体状态类 - 跳跃状态
class JumpingState extends CharacterState {
    public JumpingState(GameCharacter character) {
        super(character);
    }

    @Override
    public void handleInput(String input) {
        if ("LAND".equals(input)) {
            System.out.println("角色落地,进入站立状态。");
            character.setState(character.getStandingState());
        }
    }
}
  1. 具体状态类 - 攻击状态
class AttackingState extends CharacterState {
    public AttackingState(GameCharacter character) {
        super(character);
    }

    @Override
    public void handleInput(String input) {
        if ("END_ATTACK".equals(input)) {
            System.out.println("攻击结束,进入站立状态。");
            character.setState(character.getStandingState());
        }
    }
}
  1. 环境类 - 游戏角色
class GameCharacter {
    private CharacterState standingState;
    private CharacterState walkingState;
    private CharacterState jumpingState;
    private CharacterState attackingState;
    private CharacterState currentState;

    public GameCharacter() {
        standingState = new StandingState(this);
        walkingState = new WalkingState(this);
        jumpingState = new JumpingState(this);
        attackingState = new AttackingState(this);
        currentState = standingState;
    }

    public void handleInput(String input) {
        currentState.handleInput(input);
    }

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

    public CharacterState getStandingState() {
        return standingState;
    }

    public CharacterState getWalkingState() {
        return walkingState;
    }

    public CharacterState getJumpingState() {
        return jumpingState;
    }

    public CharacterState getAttackingState() {
        return attackingState;
    }
}
  1. 测试类
public class GameCharacterTest {
    public static void main(String[] args) {
        GameCharacter character = new GameCharacter();
        character.handleInput("WALK");
        character.handleInput("JUMP");
        character.handleInput("LAND");
        character.handleInput("ATTACK");
        character.handleInput("END_ATTACK");
    }
}

状态模式与其他设计模式的关系

状态模式与策略模式

  1. 相似点:状态模式和策略模式都涉及到通过委托来实现行为的替换。在策略模式中,不同的策略类封装了不同的算法,而在状态模式中,不同的状态类封装了不同状态下的行为。
  2. 不同点:策略模式中,客户端通常主动选择使用哪个策略,而状态模式中,状态的转换通常是由环境对象内部根据一定的条件自动进行的。策略模式更侧重于算法的替换,而状态模式更侧重于对象状态变化导致的行为变化。

状态模式与状态机

  1. 联系:状态模式可以看作是实现状态机的一种方式。状态机定义了状态、状态转换以及状态相关的行为,而状态模式通过代码实现了这些概念,将状态封装成类,并处理状态之间的转换和行为。
  2. 区别:状态机更侧重于抽象的概念和模型,而状态模式是一种具体的设计模式实现。状态模式提供了一种面向对象的方式来实现状态机,使得代码更加清晰和可维护。

状态模式在框架中的应用

Spring State Machine

Spring State Machine 是 Spring 框架中的一个模块,用于实现状态机。它基于状态模式,提供了一种声明式和编程式的方式来定义和管理状态机。

  1. 依赖引入
<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-core</artifactId>
    <version>2.5.2</version>
</dependency>
  1. 状态机配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.listener.StateMachineListener;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.state.State;

import java.util.EnumSet;

@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {

    @Override
    public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
        states.withStates()
              .initial(OrderStates.NEW)
              .states(EnumSet.allOf(OrderStates.class));
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception {
        transitions.withExternal()
              .source(OrderStates.NEW).target(OrderStates.PENDING)
              .event(OrderEvents.SUBMIT)
              .and()
              .withExternal()
              .source(OrderStates.PENDING).target(OrderStates.APPROVED)
              .event(OrderEvents.APPROVE)
              .guard(approvalGuard())
              .and()
              .withExternal()
              .source(OrderStates.PENDING).target(OrderStates.REJECTED)
              .event(OrderEvents.REJECT);
    }

    @Override
    public void configure(StateMachineConfigurationConfigurer<OrderStates, OrderEvents> config) throws Exception {
        StateMachineListener<OrderStates, OrderEvents> listener = new StateMachineListenerAdapter<>() {
            @Override
            public void stateChanged(State<OrderStates, OrderEvents> from, State<OrderStates, OrderEvents> to) {
                System.out.println("Order state changed from " + from.getId() + " to " + to.getId());
            }
        };
        config.withConfiguration()
              .listener(listener);
    }

    @Bean
    public Guard<OrderStates, OrderEvents> approvalGuard() {
        return context -> {
            // 这里可以添加审批通过的条件逻辑
            return true;
        };
    }
}
  1. 状态和事件枚举
public enum OrderStates {
    NEW, PENDING, APPROVED, REJECTED
}

public enum OrderEvents {
    SUBMIT, APPROVE, REJECT
}
  1. 使用状态机
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.StateMachine;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    @Autowired
    private StateMachine<OrderStates, OrderEvents> stateMachine;

    public void submitOrder() {
        sendEvent(OrderEvents.SUBMIT);
    }

    public void approveOrder() {
        sendEvent(OrderEvents.APPROVE);
    }

    public void rejectOrder() {
        sendEvent(OrderEvents.REJECT);
    }

    private void sendEvent(OrderEvents event) {
        Message<OrderEvents> message = MessageBuilder.withPayload(event).build();
        stateMachine.sendEvent(message);
    }
}

Struts 2 的状态管理

在 Struts 2 框架中,虽然没有直接使用状态模式,但在处理不同请求状态和页面流转时,体现了类似状态模式的思想。例如,Struts 2 的 Action 可以看作是环境对象,不同的结果类型(如“success”、“error”等)可以看作是不同的状态,而 Action 执行后的结果跳转类似于状态的转换。

  1. Struts 2 配置文件
<package name="default" namespace="/" extends="struts-default">
    <action name="login" class="com.example.LoginAction" method="execute">
        <result name="success">/success.jsp</result>
        <result name="error">/error.jsp</result>
    </action>
</package>
  1. Action 类
import com.opensymphony.xwork2.ActionSupport;

public class LoginAction extends ActionSupport {
    @Override
    public String execute() throws Exception {
        // 这里进行登录逻辑判断
        if (isLoginSuccess()) {
            return SUCCESS;
        } else {
            return ERROR;
        }
    }

    private boolean isLoginSuccess() {
        // 实际的登录验证逻辑
        return true;
    }
}

通过以上示例和分析,我们可以看到 Java 状态模式在不同场景下的应用,它为我们提供了一种有效的方式来管理对象的状态和行为,提高代码的可维护性和可扩展性。无论是简单的业务场景还是复杂的框架实现,状态模式都能发挥其独特的优势。