Java状态模式与适配器模式的基本概念

状态模式用于封装对象内部状态变化带来的行为差异,适配器模式用于让不兼容的接口能协同工作;前者解决状态依赖行为的问题,后者解决接口不匹配的对接问题。

Java 中状态模式和适配器模式不是“基本概念”层面需要背诵的定义,而是为解决两类典型问题而生的设计实践。直接说结论:**状态模式用于封装对象内部状态变化带来的行为差异,适配器模式用于让不兼容的接口能协同工作**。它们常被混淆,但出发点完全不同。

状态模式:当一个类的行为依赖于它的当前状态,并且需要在运行时根据状态改变行为

典型场景是订单系统(Order)、游戏角色(Player)、有限状态机(FSM)。核心不是“有状态”,而是“状态变了,同一方法调用结果不同”。

  • 必须把每个状态抽成独立类(如 SubmittedStateShippedState),都实现统一接口(如 OrderState
  • Context 类(如 Order)持有一个 state 引用,所有行为委托给当前 state 执行
  • 状态切换由状态自身或 Context 控制,但绝不允许外部直接 new 状态类并赋值——否则会绕过状态流转逻辑
  • 常见错误:把状态当普通枚举用,用 if/else 判断状态再分支调用,这没发挥状态模式解耦行为的优势
public interface OrderState {
    void handle(Order order);
}

public class SubmittedState implements OrderState {
    public void handle(Order order) {
        System.out.println("已提交,可取消");
        order.setState(new CanceledState()); // 状态迁移
    }
}

public class Order {
    private OrderState state = new SubmittedState();
    public void setState(OrderState state) { this.state = state; }
    public void operate() { state.handle(this); }
}

适配器模式:当已有类的接口不符合需求,又不能修改其源码时

本质是“转接头”——包装一个已有对象,对外提供新接口。分两类:类适配器(用继承,Java 不常用,因单继承限制)和对象适配器(用组合,主流做法)。

  • 适配器类要实现目标接口(Target),同时持有被适配者(Adaptee)的实例
  • 目标接口方法里,调用被适配者的方法,必要时做参数转换、结果包装或逻辑桥接
  • 不要在适配器里塞业务逻辑;它只负责“翻译”,否则职责就模糊了
  • 容易踩坑:把适配器写成工具类或静态方法,失去封装性;或强行让一个适配器适配多个不相关的接口,导致类膨胀
public interface MediaPlayer {
    void play(String audioType, String fileName);
}

public class AudioPlayer implements MediaPlayer {
    private MediaAdapter adapter;
    public void play(String audioType, String fileName) {
        if ("vlc".equalsIgnoreCase(audioType) || "mp4".equalsIgnoreCase(audioType)) {
            adapter = new MediaAdapter(audioType);
            adapter.play(audioType, fileName);
        }
    }
}

public class MediaAdapter implements MediaPlayer {
    private AdvancedMediaPlayer player;
    public MediaAdapter(String audioType) {
        if ("vlc".equalsIgnoreCase(audioType)) {
            player = new VlcPlayer();
        } else if ("mp4".equalsIgnoreCase(audioType)) {
            player = new Mp4Player();
        }
    }
    public void play(String audioType, String fileName) {
        player.play(fileName); // 委托给具体播放器
    }
}

两者关键区别:状态模式关注“谁来决定行为”,适配器模式关注“怎么对接接口”

状态模式中,ContextState 是同一体系内的协作;适配器模式中,Adapter 是外来接口与既有系统之间的隔离层。一个类可能同时用到两者(比如某个状态下的行为需要调用第三方 SDK,这时就在该状态类内部用适配器包装 SDK),但它们解决的问题域没有重叠。

真正难的是判断“该用哪个”——如果发现代码里频繁出现 switch(state) + 大段重复逻辑,优先考虑状态模式;如果引入新库后编译报错 “cannot resolve method XXX”,而你又不能改那个库,那就是适配器模式的明确信号。