状态模式与责任链模式

一个订单状态机的 if-else 噩梦

我们团队有个订单系统,状态机是这样的:

public void handle(Order order, String action) {
    if ("PAID".equals(order.getStatus())) {
        if ("DELIVER".equals(action)) {
            order.setStatus("DELIVERING");
            notifyDelivery(order);
        } else if ("CANCEL".equals(action)) {
            order.setStatus("CANCELLED");
            refund(order);
        }
    } else if ("DELIVERING".equals(order.getStatus())) {
        if ("RECEIVE".equals(action)) {
            order.setStatus("COMPLETED");
            addPoints(order);
        } else if ("REFUND".equals(action)) {
            order.setStatus("REFUNDING");
            startRefund(order);
        }
    } else if ("COMPLETED".equals(order.getStatus())) {
        if ("REFUND".equals(action)) {
            order.setStatus("REFUNDING");
            startRefund(order);
        }
    } else if ("CANCELLED".equals(order.getStatus())) {
        // 终止状态,大部分 action 无效
        throw new IllegalStateException("Order already cancelled");
    }
    // ...
}

两年下来,这个方法积累了 47 个 if-else,每个新状态都要小心翼翼地理解现有逻辑。

这就是状态模式要解决的问题:把状态的转换逻辑从条件分支中分离出来。


二、状态模式核心结构🔴

2.1 标准写法

// 订单状态接口
interface OrderState {
    void pay(OrderContext context);
    void deliver(OrderContext context);
    void receive(OrderContext context);
    void cancel(OrderContext context);
    void refund(OrderContext context);

    String getStateName();
}

// 上下文:持有当前状态
class OrderContext {
    private OrderState state;
    private final Order order;

    public OrderContext(Order order, OrderState initialState) {
        this.order = order;
        this.state = initialState;
    }

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

    public Order getOrder() {
        return order;
    }

    // 委托给当前状态处理
    public void pay() { state.pay(this); }
    public void deliver() { state.deliver(this); }
    public void receive() { state.receive(this); }
    public void cancel() { state.cancel(this); }
}

// 具体状态:待支付
class AwaitingPaymentState implements OrderState {
    @Override
    public void pay(OrderContext context) {
        context.getOrder().setStatus("PAID");
        context.setState(new PaidState());
        // 发送通知
    }

    @Override
    public void deliver(OrderContext context) {
        throw new IllegalStateException("Cannot deliver before payment");
    }

    @Override
    public void cancel(OrderContext context) {
        context.getOrder().setStatus("CANCELLED");
        context.setState(new CancelledState());
    }

    @Override
    public void receive(OrderContext context) { /* 无效 */ }
    @Override
    public void refund(OrderContext context) { /* 无效 */ }

    @Override
    public String getStateName() { return "AWAITING_PAYMENT"; }
}

// 具体状态:已支付
class PaidState implements OrderState {
    @Override
    public void pay(OrderContext context) {
        // 重复支付?忽略或报错
    }

    @Override
    public void deliver(OrderContext context) {
        context.getOrder().setStatus("DELIVERING");
        context.setState(new DeliveringState());
        notifyDelivery(context.getOrder());
    }

    @Override
    public void cancel(OrderContext context) {
        context.getOrder().setStatus("CANCELLED");
        context.setState(new CancelledState());
        refund(context.getOrder());
    }

    @Override
    public void receive(OrderContext context) { /* 无效 */ }
    @Override
    public void refund(OrderContext context) { /* 无效 */ }

    @Override
    public String getStateName() { return "PAID"; }
}

// 具体状态:配送中
class DeliveringState implements OrderState {
    @Override
    public void receive(OrderContext context) {
        context.getOrder().setStatus("COMPLETED");
        context.setState(new CompletedState());
        addPoints(context.getOrder());
    }

    @Override
    public void refund(OrderContext context) {
        context.getOrder().setStatus("REFUNDING");
        context.setState(new RefundingState());
        startRefund(context.getOrder());
    }

    // 其他方法:无效或异常
    @Override
    public void pay(OrderContext context) { /* 无效 */ }
    @Override
    public void deliver(OrderContext context) { /* 无效 */ }
    @Override
    public void cancel(OrderContext context) { /* 无效 */ }

    @Override
    public String getStateName() { return "DELIVERING"; }
}

// 其他状态...

2.2 使用方式

// 订单创建后,初始状态为待支付
Order order = new Order();
OrderContext context = new OrderContext(order, new AwaitingPaymentState());

// 支付
context.pay(); // 状态流转:待支付 -> 已支付

// 发货
context.deliver(); // 状态流转:已支付 -> 配送中

// 收货
context.receive(); // 状态流转:配送中 -> 已完成

// 如果尝试在已完成后再次发货
context.deliver(); // 抛出 IllegalStateException

2.3 状态模式 vs 策略模式

维度状态模式策略模式
状态切换状态对象内部决定下一步状态客户端外部决定
上下文角色上下文被动接收状态上下文主动选择策略
状态关系状态之间有转换关系策略之间相互独立
使用时机状态决定行为行为决定选择
// 状态模式:状态自己决定下一个状态
class PaidState {
    void deliver(OrderContext context) {
        context.setState(new DeliveringState()); // 状态自己切换
    }
}

// 策略模式:客户端决定用哪个策略
class DiscountContext {
    void setStrategy(DiscountStrategy s) { // 客户端指定
        this.strategy = s;
    }
}

【面试官心理】 状态模式和策略模式的区别是高频面试题。能说出"状态自己控制转换"和"客户端外部选择"的区别,说明候选人真正理解了两个模式的本质。


三、责任链模式🔴

3.1 标准写法

// 处理器接口
interface Handler {
    void setNext(Handler handler);
    void handle(Request request);

    default void handleNext(Request request) {
        Handler next = getNext();
        if (next != null) {
            next.handle(request);
        }
    }

    Handler getNext();
}

// 请求对象
class Request {
    private final String type;
    private final Object data;
    private boolean handled = false;

    public Request(String type, Object data) {
        this.type = type;
        this.data = data;
    }
}

// 具体处理器:认证
class AuthHandler implements Handler {
    private Handler next;

    @Override
    public void setNext(Handler handler) {
        this.next = handler;
    }

    @Override
    public Handler getNext() {
        return next;
    }

    @Override
    public void handle(Request request) {
        if (!isAuthenticated(request)) {
            throw new SecurityException("Unauthorized");
        }
        handleNext(request); // 传递给下一个处理器
    }

    private boolean isAuthenticated(Request request) {
        // 认证逻辑
        return true;
    }
}

// 具体处理器:参数校验
class ValidationHandler implements Handler {
    private Handler next;

    @Override
    public void setNext(Handler handler) {
        this.next = handler;
    }

    @Override
    public Handler getNext() {
        return next;
    }

    @Override
    public void handle(Request request) {
        if (!isValid(request)) {
            throw new IllegalArgumentException("Invalid parameters");
        }
        handleNext(request);
    }

    private boolean isValid(Request request) {
        // 校验逻辑
        return true;
    }
}

// 具体处理器:业务处理
class BusinessHandler implements Handler {
    private Handler next;

    @Override
    public void setNext(Handler handler) {
        this.next = handler;
    }

    @Override
    public Handler getNext() {
        return next;
    }

    @Override
    public void handle(Request request) {
        // 业务逻辑
        System.out.println("Processing: " + request.getType());
    }
}

3.2 链的组装

class HandlerChain {
    private Handler head;
    private Handler tail;

    public void addHandler(Handler handler) {
        if (head == null) {
            head = handler;
            tail = handler;
        } else {
            tail.setNext(handler);
            tail = handler;
        }
    }

    public void handle(Request request) {
        if (head != null) {
            head.handle(request);
        }
    }
}

// 组装
HandlerChain chain = new HandlerChain();
chain.addHandler(new AuthHandler());
chain.addHandler(new ValidationHandler());
chain.addHandler(new BusinessHandler());

// 处理请求
chain.handle(new Request("CREATE_ORDER", data));

3.3 Spring MVC 的拦截器链

Spring MVC 的 HandlerInterceptor 就是责任链模式的应用:

public interface HandlerInterceptor {
    default boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        return true; // 前置处理
    }

    default void postHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler,
                           ModelAndView modelAndView) throws Exception {
        // 后置处理
    }

    default void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) throws Exception {
        // 完成处理
    }
}

Spring MVC 按顺序调用拦截器的 preHandle,任何一个返回 false 就中断链:

请求 → Interceptor1.preHandle (true) → Interceptor2.preHandle (true)
    → Controller → Interceptor2.postHandle → Interceptor1.postHandle

四、状态模式与责任链的结合🟡

4.1 订单审批流程

// 状态模式处理状态转换
// 责任链处理审批流程

interface OrderApprovalChain {
    void approve(OrderContext context, Approver approver);
}

// 审批人(责任链中的处理器)
interface Approver {
    boolean approve(Order order);
    void setNext(Approver next);
}

// 主管审批
class TeamLeadApprover implements Approver {
    private Approver next;

    @Override
    public boolean approve(Order order) {
        if (order.getAmount() <= 10000) {
            order.approve();
            return true;
        }
        return next != null && next.approve(order);
    }

    @Override
    public void setNext(Approver next) {
        this.next = next;
    }
}

// 经理审批
class ManagerApprover implements Approver {
    private Approver next;

    @Override
    public boolean approve(Order order) {
        if (order.getAmount() <= 50000) {
            order.approve();
            return true;
        }
        return next != null && next.approve(order);
    }

    @Override
    public void setNext(Approver next) {
        this.next = next;
    }
}

五、生产避坑清单

5.1 状态模式的状态数量爆炸

// ❌ 错误:为每个状态-动作组合写一个状态类
class PaidToDeliveringState implements OrderState { } // 太细
class PaidToCancelledState implements OrderState { }  // 太细

// ✅ 正确:状态是粗粒度的,动作在状态内部决定
class PaidState implements OrderState {
    // 在一个状态类里处理所有可能的转换
}

5.2 责任链的断链问题

// ❌ 错误:忘记传递到下一个处理器
@Override
public void handle(Request request) {
    // 处理了但不传递
    process(request);
    // 忘记调用 handleNext(request);
}

// ✅ 正确:明确知道什么时候传,什么时候断
@Override
public void handle(Request request) {
    if (canHandle(request)) {
        process(request);
        // 处理完可以断链
    } else {
        handleNext(request); // 不能处理才传递
    }
}
⚠️

责任链模式最大的坑是"断链"——某个处理器处理完请求后忘记调用 handleNext,导致后续处理器永远得不到执行。解决方法是每个处理器必须显式决定是否传递。


六、面试总结

6.1 核心追问

  1. "状态模式和 if-else 相比有什么优势?" —— 消除分支、更改独立、状态内聚
  2. "状态模式和策略模式的区别?" —— 状态自己切换 vs 客户端切换
  3. "责任链模式的链断了怎么办?" —— 必须显式传递

6.2 级别差异

级别期望回答
P5能写出两种模式的基本结构
P6能说出状态模式和策略模式的区别
P7能结合 Spring MVC 分析责任链,知道状态机设计的陷阱