3.Android 设计模式 五个核心之一:策略模式 在项目中的实战

236 阅读9分钟

摘要:定义算法族并封装为独立策略,运行时动态切换。解耦算法与使用逻辑,避免条件分支,支持灵活扩展与维护。适用于多算法场景。

1.概念

1. 策略模式概念

策略模式(Strategy Pattern)  是一种行为型设计模式,它定义了一系列算法,将每个算法封装起来,并使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端。

核心思想

  • 分离变化:将频繁变化的算法从主业务逻辑中分离
  • 面向接口编程:定义统一的策略接口,不同策略实现该接口
  • 运行时切换:客户端可以在运行时动态切换不同策略

关键角色

  1. 策略接口 (Strategy) :定义算法族的公共接口
  2. 具体策略 (ConcreteStrategy) :实现策略接口的具体算法
  3. 上下文 (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图

策略.png

 核心角色说明
角色职责示例
Context(环境类)持有策略接口的引用,提供切换和执行策略的方法ListView(Android)8
Strategy(抽象策略)定义算法的公共接口(通常为接口或抽象类)ListAdapter(Android)8
ConcreteStrategy(具体策略)实现策略接口,封装具体算法逻辑ArrayAdapter/BaseAdapter(Android)
 UML 关键关系
  1. 关联关系(Association)
    Context 持有 Strategy 的引用(如成员变量),通过组合实现动态算法切换1。
  2. 实现关系(Realization)
    具体策略类(如 ConcreteStrategyA)实现 Strategy 接口,提供算法的具体实现28。
  3. 开闭原则支持
    新增算法只需添加新的 ConcreteStrategy,无需修改 Context 或已有策略

4.项目的例子和没用设计模式的对比

场景描述

策略模式在语音项目中的其他应用场景

  • 1.语音合成(TTS)引擎选择
  • 2.噪声消除算法选择
  • 3.语音唤醒词检测策略
  • 4.支持多种语音识别引擎:

在语音助手中,我们需要支持多种语音识别引擎:

  1. 本地离线识别:快速响应,但识别率较低
  2. 云端AI识别:识别率高,但需要网络
  3. 混合模式:先尝试本地识别,失败后再用云端
  4. 自定义引擎:特定领域优化(如医疗、法律)

不同场景需要不同识别策略:

  • 无网络时强制使用本地识别
  • 重要会议时使用高精度云端识别
  • 医疗场景使用专业术语优化引擎
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());
    }




}
整体的架构图

策略2.png

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…