摘要:定义算法族并封装为独立策略,运行时动态切换。解耦算法与使用逻辑,避免条件分支,支持灵活扩展与维护。适用于多算法场景。
1.概念
1. 策略模式概念
策略模式(Strategy Pattern) 是一种行为型设计模式,它定义了一系列算法,将每个算法封装起来,并使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端。
核心思想:
- 分离变化:将频繁变化的算法从主业务逻辑中分离
- 面向接口编程:定义统一的策略接口,不同策略实现该接口
- 运行时切换:客户端可以在运行时动态切换不同策略
关键角色:
- 策略接口 (Strategy) :定义算法族的公共接口
- 具体策略 (ConcreteStrategy) :实现策略接口的具体算法
- 上下文 (Context) :持有策略引用,通过策略接口与具体策略交互
何时使用策略模式:
- 当有多种算法实现相同功能时
- 需要在运行时切换不同算法时
- 需要隔离算法实现和使用逻辑时
- 当算法可能在未来扩展或变化时
2.在Android源码中的应用场景
2.1 Android 动画插值器(Interpolator)
// 策略接口
public interface Interpolator {
float getInterpolation(float input);
}
// 具体策略
public class LinearInterpolator implements Interpolator {
public float getInterpolation(float input) {
return input;
}
}
public class AccelerateInterpolator implements Interpolator {
public float getInterpolation(float input) {
return input * input;
}
}
// 上下文
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f);
animator.setInterpolator(new AccelerateInterpolator()); // 设置策略
animator.start();
2.2 其他 Android 应用场景
- 图片加载策略:Glide 的 DiskCacheStrategy
2.3 在ARouter中的使用场景
- 应用场景:路由跳转逻辑
- 实现:
根据Postcard中目标类型(Activity、Fragment、Service)动态选择不同的跳转策略。 - 源码逻辑:
LogisticsCenter 根据 RouteMeta 类型选择处理策略
if (routeMeta.getType() == RouteType.ACTIVITY) {
// 启动 Activity 的策略
} else if (routeMeta.getType() == RouteType.FRAGMENT) {
// 返回 Fragment 的策略
}
策略模式允许在运行时选择算法的行为。代码中有这样的逻辑:
if (obj instanceof IRouteRoot) {
registerRouteRoot((IRouteRoot) obj);
} else if (obj instanceof IProviderGroup) {
registerProvider((IProviderGroup) obj);
} else if (obj instanceof IInterceptorGroup) {
registerInterceptor((IInterceptorGroup) obj);
} else {
logger.info(TAG, "register failed, class name: " + className
+ " should implements one of IRouteRoot/IProviderGroup/IInterceptorGroup.");
}
此代码依据对象的类型来选择不同的注册策略,这正是策略模式的典型应用场景。
3.UML图
核心角色说明
| 角色 | 职责 | 示例 |
|---|---|---|
Context(环境类) | 持有策略接口的引用,提供切换和执行策略的方法 | ListView(Android)8 |
Strategy(抽象策略) | 定义算法的公共接口(通常为接口或抽象类) | ListAdapter(Android)8 |
ConcreteStrategy(具体策略) | 实现策略接口,封装具体算法逻辑 | ArrayAdapter/BaseAdapter(Android) |
UML 关键关系
- 关联关系(Association)
Context持有Strategy的引用(如成员变量),通过组合实现动态算法切换1。 - 实现关系(Realization)
具体策略类(如ConcreteStrategyA)实现Strategy接口,提供算法的具体实现28。 - 开闭原则支持
新增算法只需添加新的ConcreteStrategy,无需修改Context或已有策略
4.项目的例子和没用设计模式的对比
场景描述
策略模式在语音项目中的其他应用场景
- 1.语音合成(TTS)引擎选择
- 2.噪声消除算法选择
- 3.语音唤醒词检测策略
- 4.支持多种语音识别引擎:
在语音助手中,我们需要支持多种语音识别引擎:
- 本地离线识别:快速响应,但识别率较低
- 云端AI识别:识别率高,但需要网络
- 混合模式:先尝试本地识别,失败后再用云端
- 自定义引擎:特定领域优化(如医疗、法律)
不同场景需要不同识别策略:
- 无网络时强制使用本地识别
- 重要会议时使用高精度云端识别
- 医疗场景使用专业术语优化引擎
4.1 没有使用策略模式
public class SpeechRecognizerWithoutPattern {
private final LocalSpeechRecognizer localRecognizer;
private final CloudSpeechClient cloudClient;
private final MedicalTerminologyEnhancer medicalEnhancer;
// 当前模式
public static final int MODE_LOCAL = 0;
public static final int MODE_CLOUD = 1;
public static final int MODE_HYBRID = 2;
public static final int MODE_MEDICAL = 3;
private int currentMode = MODE_HYBRID;
public SpeechRecognizerWithoutPattern() {
localRecognizer = new LocalSpeechRecognizer();
cloudClient = new CloudSpeechClient("API_KEY_HERE");
medicalEnhancer = new MedicalTerminologyEnhancer();
}
public void setMode(int mode) {
this.currentMode = mode;
}
public String recognizeSpeech(byte[] audioData) {
switch (currentMode) {
case MODE_LOCAL:
return localRecognizer.recognizeOffline(audioData);
case MODE_CLOUD:
if (!NetworkUtils.isNetworkAvailable()) {
throw new IllegalStateException("网络不可用");
}
return cloudClient.recognizeOnline(audioData);
case MODE_HYBRID:
String localResult = localRecognizer.recognizeOffline(audioData);
if (TextUtils.isEmpty(localResult) || localResult.equals("unknown")) {
if (NetworkUtils.isNetworkAvailable()) {
return cloudClient.recognizeOnline(audioData);
}
}
return localResult;
case MODE_MEDICAL:
if (!NetworkUtils.isNetworkAvailable()) {
throw new IllegalStateException("网络不可用");
}
String text = cloudClient.recognizeOnline(audioData);
return medicalEnhancer.enhanceMedicalTerms(text);
default:
throw new IllegalStateException("未知模式");
}
}
public String getCurrentModeName() {
switch (currentMode) {
case MODE_LOCAL: return "本地离线识别";
case MODE_CLOUD: return "云端AI识别";
case MODE_HYBRID: return "混合识别模式";
case MODE_MEDICAL: return "医疗专用识别";
default: return "未知模式";
}
}
}
4.2 使用策略模式
// 云端AI识别策略
public class CloudRecognitionStrategy implements SpeechRecognitionStrategy {
private final CloudSpeechClient client;
public CloudRecognitionStrategy(String apiKey) {
this.client = new CloudSpeechClient(apiKey);
}
@Override
public String recognize(byte[] audioData) {
return client.recognizeOnline(audioData);
}
@Override
public String getStrategyName() {
return "云端AI识别";
}
@Override
public boolean isAvailable() {
return NetworkUtils.isNetworkAvailable(); // 需要网络
}
}
public class CloudSpeechClient {
String key;
public CloudSpeechClient(String key) {
this.key = key;
}
public String recognizeOnline(byte[] audioData) {
return "";
}
}
// 混合识别策略
public class HybridRecognitionStrategy implements SpeechRecognitionStrategy {
private final LocalRecognitionStrategy localStrategy;
private final CloudRecognitionStrategy cloudStrategy;
public HybridRecognitionStrategy(String apiKey) {
this.localStrategy = new LocalRecognitionStrategy();
this.cloudStrategy = new CloudRecognitionStrategy(apiKey);
}
@Override
public String recognize(byte[] audioData) {
// 先尝试本地识别
String result = localStrategy.recognize(audioData);
if (TextUtils.isEmpty(result) || result.equals("unknown")) {
// 本地识别失败,尝试云端
if (cloudStrategy.isAvailable()) {
result = cloudStrategy.recognize(audioData);
}
}
return result;
}
@Override
public String getStrategyName() {
return "混合识别模式";
}
@Override
public boolean isAvailable() {
return localStrategy.isAvailable() || cloudStrategy.isAvailable();
}
}
// 本地离线识别策略
public class LocalRecognitionStrategy implements SpeechRecognitionStrategy {
private final LocalSpeechRecognizer recognizer;
public LocalRecognitionStrategy() {
this.recognizer = new LocalSpeechRecognizer();
}
@Override
public String recognize(byte[] audioData) {
return recognizer.recognizeOffline(audioData);
}
@Override
public String getStrategyName() {
return "本地离线识别";
}
@Override
public boolean isAvailable() {
return true; // 本地引擎始终可用
}
}
public class LocalSpeechRecognizer {
public String recognizeOffline(byte[] audioData) {
return "";
}
}
// 医疗领域专用识别策略
public class MedicalRecognitionStrategy implements SpeechRecognitionStrategy {
private final CloudSpeechClient client;
private final MedicalTerminologyEnhancer enhancer;
public MedicalRecognitionStrategy(String apiKey) {
this.client = new CloudSpeechClient(apiKey);
this.enhancer = new MedicalTerminologyEnhancer();
}
@Override
public String recognize(byte[] audioData) {
String text = client.recognizeOnline(audioData);
return enhancer.enhanceMedicalTerms(text);
}
@Override
public String getStrategyName() {
return "医疗专用识别";
}
@Override
public boolean isAvailable() {
return NetworkUtils.isNetworkAvailable();
}
}
public class MedicalTerminologyEnhancer {
public String enhanceMedicalTerms(String text) {
return "";
}
}
public class NetworkUtils {
public static boolean isNetworkAvailable() {
return true;
}
}
// 语音识别上下文
public class SpeechRecognitionContext {
private SpeechRecognitionStrategy strategy;
private final Map<String, SpeechRecognitionStrategy> strategyCache = new HashMap<>();
public SpeechRecognitionContext() {
// 初始化默认策略
setStrategy("hybrid");
}
public void setStrategy(String strategyKey) {
if (strategyCache.containsKey(strategyKey)) {
strategy = strategyCache.get(strategyKey);
return;
}
switch (strategyKey) {
case "local":
strategy = new LocalRecognitionStrategy();
break;
case "cloud":
strategy = new CloudRecognitionStrategy("API_KEY_HERE");
break;
case "hybrid":
strategy = new HybridRecognitionStrategy("API_KEY_HERE");
break;
case "medical":
strategy = new MedicalRecognitionStrategy("MEDICAL_API_KEY");
break;
default:
throw new IllegalArgumentException("未知策略: " + strategyKey);
}
strategyCache.put(strategyKey, strategy);
}
public String recognizeSpeech(byte[] audioData) {
if (strategy == null || !strategy.isAvailable()) {
throw new IllegalStateException("没有可用的语音识别策略");
}
return strategy.recognize(audioData);
}
public String getCurrentStrategyName() {
return strategy != null ? strategy.getStrategyName() : "未设置策略";
}
}
// 语音识别策略接口
public interface SpeechRecognitionStrategy {
/**
* 识别语音
* @param audioData 音频数据
* @return 识别结果文本
*/
String recognize(byte[] audioData);
/**
* 获取策略名称
*/
String getStrategyName();
/**
* 是否可用(如网络依赖)
*/
boolean isAvailable();
}
调用逻辑:
public class VoiceAssistant {
private final SpeechRecognitionContext recognitionContext = new SpeechRecognitionContext();
public void onVoiceCommandReceived(byte[] audioData) {
// 根据当前场景动态切换策略
if (isMedicalContext()) {
recognitionContext.setStrategy("medical");
} else if (!NetworkUtils.isNetworkAvailable()) {
recognitionContext.setStrategy("local");
} else if (isHighAccuracyRequired()) {
recognitionContext.setStrategy("cloud");
}
// 执行识别
String text = recognitionContext.recognizeSpeech(audioData);
processCommand(text);
}
private void processCommand(String text) {
// 处理识别结果
System.out.println("识别结果: " + text);
System.out.println("使用策略: " + recognitionContext.getCurrentStrategyName());
}
// 示例辅助方法
private boolean isMedicalContext() {
// 判断是否在医疗场景
return false;
}
private boolean isHighAccuracyRequired() {
// 判断是否需要高精度
return true;
}
private final SpeechRecognizerWithoutPattern recognizer = new SpeechRecognizerWithoutPattern();
public void onVoiceCommandReceivedWithout(byte[] audioData) {
// 根据当前场景设置模式
if (isMedicalContext()) {
recognizer.setMode(SpeechRecognizerWithoutPattern.MODE_MEDICAL);
} else if (!NetworkUtils.isNetworkAvailable()) {
recognizer.setMode(SpeechRecognizerWithoutPattern.MODE_LOCAL);
} else if (isHighAccuracyRequired()) {
recognizer.setMode(SpeechRecognizerWithoutPattern.MODE_CLOUD);
}
// 执行识别
String text;
try {
text = recognizer.recognizeSpeech(audioData);
} catch (Exception e) {
// 错误处理...
text = "识别失败";
}
processCommandWithout(text);
}
private void processCommandWithout(String text) {
// 处理识别结果
System.out.println("识别结果: " + text);
System.out.println("使用模式: " + recognizer.getCurrentModeName());
}
}
整体的架构图
4.3 添加一个新的,需要怎么修改,哪个更好用 ,实际场景对比
场景:添加金融领域专用识别引擎
-
策略模式实现:
// 1. 创建新策略类 public class FinanceRecognitionStrategy implements SpeechRecognitionStrategy { // 实现金融专用识别逻辑 } // 2. 在上下文中注册新策略 public class SpeechRecognitionContext { // ... public void setStrategy(String strategyKey) { // ... case "finance": strategy = new FinanceRecognitionStrategy("FINANCE_API_KEY"); break; // ... } } -
非策略模式实现:
public class SpeechRecognizerWithoutPattern { // ... private static final int MODE_FINANCE = 4; // 添加新常量 public String recognizeSpeech(byte[] audioData) { switch (currentMode) { // ... 现有case case MODE_FINANCE: // 添加新分支 if (!NetworkUtils.isNetworkAvailable()) { throw new IllegalStateException("网络不可用"); } String text = cloudClient.recognizeOnline(audioData); return financeEnhancer.enhanceFinancialTerms(text); // ... } } }
4.4 代码结构与可维护性
| 特性 | 策略模式实现 | 非策略模式实现 |
|---|---|---|
| 新增识别引擎 | 添加新策略类,修改上下文注册逻辑 | 修改大switch语句,添加新case分支 |
| 修改现有引擎 | 修改具体策略类,不影响其他代码 | 需在大型方法中定位修改点,风险高 |
| 条件判断复杂度 | 上下文类中简单选择策略 | 大型switch-case结构,逻辑复杂 |
| 错误隔离 | 一个策略出错不影响其他策略 | 整个识别方法可能崩溃 |
| 单元测试 | 可单独测试每个策略 | 必须测试整个大方法 |
4.5 扩展性与灵活性
| 特性 | 策略模式实现 | 非策略模式实现 |
|---|---|---|
| 动态扩展 | 运行时添加新策略 | 需修改源代码,重新编译 |
| 策略组合 | 容易实现组合策略(如混合模式) | 难以实现,需大量重复代码 |
| 条件复用 | 策略可在不同上下文中复用 | 逻辑绑定在单个类中 |
| 多语言支持 | 轻松添加不同语言的识别策略 | 需在大型方法中添加更多分支 |
| 第三方引擎集成 | 通过适配器模式轻松集成 | 需要修改核心识别逻辑 |
5.优点
避免多重条件语句
- 问题:传统实现中,不同的行为可能需要通过条件分支(如
if-else或switch)来选择算法。 - 解决:策略模式将每个算法封装成独立的策略类,消除了复杂的条件判断,代码更清晰。
策略模式通过封装变化、解耦依赖和动态组合,提供了一种优雅的扩展和维护算法的方式,是面向对象设计中“多用组合,少用继承”原则的典型实践。
6.和相似的设计模式的区别
策略模式 vs. 工厂模式
关键区别: 策略让你改变做事方式,工厂帮你获得做事工具。
- 策略模式:Context持有Strategy,通过委托执行行为 (行为,实现类)
- 工厂模式:Factory创建Product,客户端使用产品对象(创建对象的过程)
策略模式和工厂模式的核心区别在于:
- 策略模式解决"怎么做"的问题
关注行为的灵活性和可替换性,让你能在运行时切换不同的算法实现。 - 工厂模式解决"谁来创建"的问题
关注对象的创建过程,将实例化逻辑与使用逻辑分离
| 维度 | 策略模式 | 工厂模式 |
|---|---|---|
| 模式类型 | 行为型 | 创建型 |
| 关注点 | 算法执行 | 对象创建 |
| 运行时变化 | 可动态切换策略 | 创建后对象通常固定 |
| 协作方式 | 上下文使用策略执行操作 | 客户端使用工厂创建对象 |
| 典型应用 | 支付/压缩算法切换 | 创建不同数据库连接对象 |
demo地址: github.com/pengcaihua1…