一句话总结:
策略模式像「随时换刀」—— 不同算法灵活切换,状态模式像「自动档位」—— 行为随状态自动变化,Android 系统里两者各管一摊,一个拼工具,一个管流程!
一、策略模式(Strategy)—— 万能工具包
核心:动态替换算法,把策略抽象成可插拔的模块
Android 源码例子:动画插值器 Interpolator
// frameworks/base/core/java/android/view/animation/Interpolator.java
public interface Interpolator {
float getInterpolation(float input);
}
// 具体策略:线性插值
public class LinearInterpolator implements Interpolator {
public float getInterpolation(float input) { return input; }
}
// 具体策略:弹性插值
public class BounceInterpolator implements Interpolator {
public float getInterpolation(float input) { /* 弹性计算逻辑 */ }
}
// 使用方式:运行时切换策略
ObjectAnimator anim = ObjectAnimator.ofFloat(view, "translationX", 0, 100);
anim.setInterpolator(new BounceInterpolator()); // 换策略就像换工具
底层原理:
- 动画系统通过
TimeInterpolator(即Interpolator)接口分离算法 - 运行时通过
setInterpolator()动态改变插值策略
使用场景:
- 多种算法需要灵活切换(如加密算法、图片加载策略)
- 需要隔离算法实现与使用者(如
RecyclerView的布局管理器)
特点:
- 客户端主动选择策略
- 策略之间彼此独立,无状态依赖
二、状态模式(State)—— 智能状态机
核心:行为随状态自动改变,状态转换由内部逻辑驱动
Android 源码例子:WifiManager 的状态管理
// frameworks/base/wifi/java/android/net/wifi/WifiManager.java
private void updateState(int newState) {
synchronized (mLock) {
if (mWifiState != newState) {
mWifiState = newState;
// 状态变更触发行为
if (newState == WIFI_STATE_DISABLED) {
resetNotification();
} else if (newState == WIFI_STATE_ENABLED) {
checkAndSendScanAvailableBroadcast();
}
}
}
}
// 状态驱动的回调
private void handleScanResultsAvailable() {
if (mWifiState != WIFI_STATE_ENABLED) return; // 行为受状态控制
// 执行扫描结果处理...
}
底层原理:
- 定义
WIFI_STATE_DISABLED、WIFI_STATE_ENABLED等状态常量 - 状态变更时自动触发关联操作(如广播通知、清理资源)
使用场景:
- 对象有多个状态且行为差异大(如播放器的播放/暂停/停止)
- 状态转换逻辑复杂(如订单的待付款/已发货/已完成)
特点:
- 状态转换由对象内部管理
- 不同状态对应不同行为实现
三、模式对比表
| 维度 | 策略模式 | 状态模式 |
|---|---|---|
| 核心目标 | 灵活替换算法 | 管理状态驱动的行为变化 |
| 变化触发方 | 客户端主动设置 | 对象内部状态自动触发 |
| 关注重点 | 不同算法的实现 | 状态转换逻辑和行为映射 |
| Android案例 | Interpolator、LayoutManager | Wifi 状态、MediaPlayer 生命周期 |
| 代码复杂度 | 低(策略独立) | 高(需处理状态转换关系) |
四、源码级原理剖析
1. 策略模式在 RecyclerView 布局的应用
// frameworks/base/core/java/androidx/recyclerview/widget/RecyclerView.java
public void setLayoutManager(LayoutManager layout) {
mLayout = layout; // 动态切换布局策略
requestLayout();
}
// LinearLayoutManager(策略实现)
public class LinearLayoutManager extends LayoutManager {
@Override
public void onLayoutChildren(Recycler recycler, State state) {
// 线性布局的具体算法
}
}
// GridLayoutManager(另一策略实现)
public class GridLayoutManager extends LinearLayoutManager {
@Override
public void onLayoutChildren(Recycler recycler, State state) {
// 网格布局的具体算法
}
}
- 客户端通过
setLayoutManager()自由切换布局策略
2. 状态模式在 MediaPlayer 生命周期中的体现
// frameworks/base/media/java/android/media/MediaPlayer.java
private int mCurrentState = IDLE; // 当前状态
private void start() throws IllegalStateException {
if (mCurrentState != PREPARED) {
throw new IllegalStateException("prepare() must be called first");
}
// 开始播放...
mCurrentState = STARTED;
}
private void reset() {
// 重置时清理资源
if (mCurrentState == PLAYING) {
stop();
}
mCurrentState = IDLE;
}
- 每个方法都检查当前状态,状态不符则抛出异常
五、使用场景口诀
「算法多变用策略,状态驱动上状态
实际开发选择指南:
- 图片加载库的缓存策略 → 策略模式(内存缓存、磁盘缓存灵活切换)
- 登录模块的验证方式 → 策略模式(短信、密码、指纹多种验证)
- 播放器控制 → 状态模式(播放、暂停、停止状态联动)
- 订单流程管理 → 状态模式(待支付、已发货、已完成状态转换)
注意事项:
- 策略模式注意避免策略类膨胀(超过 5 个策略建议用工厂管理)
- 状态模式要确保状态转换的完整性(防止出现非法状态)
- 复杂状态机建议用状态表(二维数组表示状态转换规则)
六、逆向思维:如何区分两种模式?
问自己两个问题:
-
行为变化的原因是什么?
- 如果是外部指定不同算法 → 策略模式
- 如果是内部状态触发不同行为 → 状态模式
-
是否需要管理状态间的转换逻辑?
- 不需要 → 策略模式
- 需要 → 状态模式
经典误用案例:
把网络重试机制(根据失败次数切换策略)硬写成状态模式,实际上更适合用策略模式 + 简单计数器
总结口诀:
「策略换算法,状态改行为
外选策略工具多,内变状态流程转」