模式定义

状态模式是一种行为型设计模式,它允许一个对象在其内部状态改变时改变它的行为。状态模式将对象的状态封装成独立的类,并将请求委托给当前状态对象,行为会随着状态的改变而改变。状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况,把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。

模式结构

graph TD A[Context] --> B[State] B --> C[ConcreteStateA] B --> D[ConcreteStateB]

实现方式

基础实现

定义状态接口,实现具体状态类和上下文类。

// 状态接口
public interface State {
    void insertQuarter();
    void ejectQuarter();
    void turnCrank();
    void dispense();
}

// 糖果机上下文
class GumballMachine {
    State soldOutState;
    State noQuarterState;
    State hasQuarterState;
    State soldState;
    
    State state;
    int count = 0;
    
    public GumballMachine(int numberGumballs) {
        soldOutState = new SoldOutState(this);
        noQuarterState = new NoQuarterState(this);
        hasQuarterState = new HasQuarterState(this);
        soldState = new SoldState(this);
        
        this.count = numberGumballs;
        if (numberGumballs > 0) {
            state = noQuarterState;
        } else {
            state = soldOutState;
        }
    }
    
    public void insertQuarter() {
        state.insertQuarter();
    }
    
    public void ejectQuarter() {
        state.ejectQuarter();
    }
    
    public void turnCrank() {
        state.turnCrank();
        state.dispense();
    }
    
    void setState(State state) {
        this.state = state;
    }
    
    void releaseBall() {
        System.out.println("糖果弹出...");
        if (count != 0) {
            count = count - 1;
        }
    }
    
    // getters
    public State getSoldOutState() { return soldOutState; }
    public State getNoQuarterState() { return noQuarterState; }
    public State getHasQuarterState() { return hasQuarterState; }
    public State getSoldState() { return soldState; }
    public int getCount() { return count; }
}

// 无硬币状态
class NoQuarterState implements State {
    GumballMachine gumballMachine;
    
    public NoQuarterState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
    
    @Override
    public void insertQuarter() {
        System.out.println("你投入了一个硬币");
        gumballMachine.setState(gumballMachine.getHasQuarterState());
    }
    
    @Override
    public void ejectQuarter() {
        System.out.println("你还没有投入硬币");
    }
    
    @Override
    public void turnCrank() {
        System.out.println("你转动了曲柄,但没有投入硬币");
    }
    
    @Override
    public void dispense() {
        System.out.println("你需要先支付");
    }
}

// 有硬币状态
class HasQuarterState implements State {
    GumballMachine gumballMachine;
    
    public HasQuarterState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
    
    @Override
    public void insertQuarter() {
        System.out.println("你已经投过币了,不能再投币");
    }
    
    @Override
    public void ejectQuarter() {
        System.out.println("退还硬币");
        gumballMachine.setState(gumballMachine.getNoQuarterState());
    }
    
    @Override
    public void turnCrank() {
        System.out.println("你转动了曲柄...");
        gumballMachine.setState(gumballMachine.getSoldState());
    }
    
    @Override
    public void dispense() {
        System.out.println("还未转动曲柄");
    }
}

// 售卖状态
class SoldState implements State {
    GumballMachine gumballMachine;
    
    public SoldState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
    
    @Override
    public void insertQuarter() {
        System.out.println("请等待,正在发放糖果");
    }
    
    @Override
    public void ejectQuarter() {
        System.out.println("对不起,你已经转动了曲柄");
    }
    
    @Override
    public void turnCrank() {
        System.out.println("转动两次不会得到两颗糖果!");
    }
    
    @Override
    public void dispense() {
        gumballMachine.releaseBall();
        if (gumballMachine.getCount() > 0) {
            gumballMachine.setState(gumballMachine.getNoQuarterState());
        } else {
            System.out.println("糖果已售罄");
            gumballMachine.setState(gumballMachine.getSoldOutState());
        }
    }
}

// 售罄状态
class SoldOutState implements State {
    GumballMachine gumballMachine;
    
    public SoldOutState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
    
    @Override
    public void insertQuarter() {
        System.out.println("糖果已售罄,无法投币");
    }
    
    @Override
    public void ejectQuarter() {
        System.out.println("你没有投币,无法退还");
    }
    
    @Override
    public void turnCrank() {
        System.out.println("糖果已售罄,无法转动曲柄");
    }
    
    @Override
    public void dispense() {
        System.out.println("糖果已售罄");
    }
}

使用示例

public class StatePatternDemo {
    public static void main(String[] args) {
        GumballMachine gumballMachine = new GumballMachine(5);
        
        System.out.println("糖果机状态: " + gumballMachine);
        
        gumballMachine.insertQuarter();
        gumballMachine.turnCrank();
        
        System.out.println("糖果机状态: " + gumballMachine);
        
        gumballMachine.insertQuarter();
        gumballMachine.turnCrank();
        
        System.out.println("糖果机状态: " + gumballMachine);
        
        gumballMachine.insertQuarter();
        gumballMachine.turnCrank();
        gumballMachine.insertQuarter();
        gumballMachine.turnCrank();
        
        System.out.println("糖果机状态: " + gumballMachine);
    }
}

经典案例

1. 线程状态管理

Java线程的状态管理体现了状态模式的思想:

// 线程状态枚举
enum ThreadState {
    NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED
}

// 线程上下文
class MyThread {
    private ThreadState state = ThreadState.NEW;
    
    public void start() {
        if (state == ThreadState.NEW) {
            state = ThreadState.RUNNABLE;
            System.out.println("线程启动");
        } else {
            System.out.println("线程已在运行或已终止");
        }
    }
    
    public void sleep(long millis) {
        if (state == ThreadState.RUNNABLE) {
            state = ThreadState.TIMED_WAITING;
            System.out.println("线程进入睡眠状态");
            // 模拟睡眠
            try {
                Thread.sleep(millis);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            state = ThreadState.RUNNABLE;
            System.out.println("线程唤醒");
        }
    }
    
    public void terminate() {
        if (state != ThreadState.TERMINATED) {
            state = ThreadState.TERMINATED;
            System.out.println("线程终止");
        }
    }
    
    public ThreadState getState() {
        return state;
    }
}

2. TCP连接状态

网络编程中的TCP连接状态管理是状态模式的典型应用:

interface TCPState {
    void open(TCPConnection connection);
    void close(TCPConnection connection);
    void acknowledge(TCPConnection connection);
}

class TCPConnection {
    private TCPState state;
    
    public TCPConnection() {
        state = new ClosedState();
    }
    
    public void open() {
        state.open(this);
    }
    
    public void close() {
        state.close(this);
    }
    
    public void acknowledge() {
        state.acknowledge(this);
    }
    
    public void setState(TCPState state) {
        this.state = state;
    }
}

class ClosedState implements TCPState {
    @Override
    public void open(TCPConnection connection) {
        System.out.println("打开连接");
        connection.setState(new OpenState());
    }
    
    @Override
    public void close(TCPConnection connection) {
        System.out.println("连接已关闭");
    }
    
    @Override
    public void acknowledge(TCPConnection connection) {
        System.out.println("连接关闭状态下无法确认");
    }
}

class OpenState implements TCPState {
    @Override
    public void open(TCPConnection connection) {
        System.out.println("连接已打开");
    }
    
    @Override
    public void close(TCPConnection connection) {
        System.out.println("关闭连接");
        connection.setState(new ClosedState());
    }
    
    @Override
    public void acknowledge(TCPConnection connection) {
        System.out.println("确认数据包");
    }
}

应用场景

优缺点

优点

  • 封装了状态转换规则:将状态转换逻辑封装在状态类中,易于理解和维护
  • 符合开闭原则:增加新的状态类不需要修改其他状态类和上下文类
  • 消除庞大的条件分支语句:将复杂的条件判断分散到不同的状态类中
  • 使状态转换显式化:状态转换通过设置不同的状态对象来实现
  • 状态对象可共享:状态对象可以被多个上下文对象共享

缺点

  • 增加类的数量:每增加一个状态就需要增加一个类
  • 状态逻辑分散:状态逻辑分布在多个类中,增加了系统复杂性
  • 对象开销:每个状态都是一个对象,会增加系统开销