rokevin
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
  • 策略模式(Strategy Pattern)

  • 一、定义
  • 二、意图
  • 三、结构
  • 四、类图(Mermaid 版)
  • 五、时序图(Mermaid 版)
  • 六、模式分析
    • 6.1 核心逻辑:封装与委托
    • 6.2 与状态模式的区别
  • 七、优点
  • 八、缺点
  • 九、适用环境
  • 十、模式扩展
    • 10.1 策略工厂
    • 10.2 带缓存的策略
    • 10.3 策略链
  • 十一、模式应用
    • 11.1 Java 标准库
    • 11.2 框架应用
    • 11.3 业务场景
  • 十二、Android 中的应用
    • 12.1 动画插值器(Interpolator)
    • 12.2 布局管理器(LayoutManager)
    • 12.3 权限请求策略
  • 十三、代码实现(Java 版)
    • 13.1 步骤 1:定义抽象策略接口
    • 13.2 步骤 2:实现具体策略类
    • 13.3 步骤 3:实现环境类
    • 13.4 步骤 4:客户端测试
    • 13.5 运行结果
  • 十四、总结

策略模式(Strategy Pattern)

策略模式是一种行为型设计模式,它通过将算法或行为封装成独立的策略对象,使它们可以相互替换,从而实现算法的动态切换,同时避免使用多重条件判断语句。这种模式在需要灵活选择不同处理方式的场景中非常有用。

一、定义

官方定义: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. (定义一系列算法,将每个算法都封装起来,并且使它们之间可以互换。策略模式使算法可以独立于使用它的客户端而变化。)

通俗理解: 策略模式就像我们日常生活中的 "锦囊妙计"—— 针对同一个问题(如出行),有多种解决方案(步行、骑车、开车、乘公交),我们可以根据实际情况(距离、天气、时间)选择合适的策略,并且可以随时切换。

二、意图

策略模式的核心目标是解决以下问题:

  1. 消除多重条件判断:避免使用if-else或switch语句来选择不同的算法,提高代码可维护性;
  2. 算法的封装与复用:将不同算法封装成独立组件,便于单独维护和复用;
  3. 动态切换算法:允许在运行时根据需求灵活切换算法,无需修改客户端代码;
  4. 开放 - 封闭原则:新增算法时无需修改现有代码,只需添加新的策略类。

三、结构

策略模式包含 3 个核心角色:

角色名称核心职责
抽象策略(Strategy)定义所有支持的算法的公共接口,通常是一个接口或抽象类,声明算法的执行方法。
具体策略(ConcreteStrategy)实现抽象策略定义的接口,包含具体的算法逻辑。
环境类(Context)持有一个策略对象的引用,负责策略的切换和调用,是客户端与策略之间的中间层。

四、类图(Mermaid 版)

以 "支付系统" 为场景,展示策略模式的结构:

classDiagram
    direction TB
    
    %% 1. 抽象策略:支付策略接口
    class PaymentStrategy {
        <<interface>>
        + pay(amount: double): boolean  // 支付方法
    }
    
    %% 2. 具体策略:各种支付方式
    class AliPayStrategy {
        + pay(amount: double): boolean
    }
    
    class WechatPayStrategy {
        + pay(amount: double): boolean
    }
    
    class CreditCardStrategy {
        + pay(amount: double): boolean
    }
    
    %% 3. 环境类:支付上下文
    class PaymentContext {
        - strategy: PaymentStrategy  // 持有策略引用
        + setStrategy(strategy: PaymentStrategy): void  // 设置策略
        + executePayment(amount: double): boolean  // 执行支付
    }
    
    %% 关系
    PaymentContext "1" --> "使用" PaymentStrategy : 依赖 >
    AliPayStrategy ..|> PaymentStrategy : 实现
    WechatPayStrategy ..|> PaymentStrategy : 实现
    CreditCardStrategy ..|> PaymentStrategy : 实现

五、时序图(Mermaid 版)

以 "用户选择支付宝支付" 为场景,展示策略模式的交互流程:

sequenceDiagram
    participant Client as 客户端(用户)
    participant Context as 环境类(PaymentContext)
    participant AliPay as 具体策略(AliPayStrategy)
    
    %% 1. 初始化
    Client->>AliPay: 1. 创建支付宝策略对象
    Client->>Context: 2. 创建支付上下文
    Client->>Context: 3. setStrategy(aliPay)  // 设置策略
    
    %% 2. 执行支付
    Client->>Context: 4. executePayment(99.0)  // 发起支付请求
    Context->>AliPay: 5. pay(99.0)  // 调用策略的支付方法
    AliPay-->>Context: 6. 返回支付结果(成功/失败)
    Context-->>Client: 7. 返回支付结果

六、模式分析

6.1 核心逻辑:封装与委托

策略模式的关键在于将算法的定义与使用分离:

  • 算法的具体实现封装在具体策略类中;
  • 环境类持有策略的引用,并将具体的算法调用委托给策略对象;
  • 客户端通过环境类间接使用策略,无需直接与具体策略交互。

这种设计使得:

  • 算法可以独立于客户端变化;
  • 新增或替换算法只需操作策略对象,无需修改客户端或环境类代码;
  • 避免了使用多重条件判断语句,提高代码可读性。

6.2 与状态模式的区别

策略模式常与状态模式混淆,两者都使用了封装和委托的思想,但意图不同:

对比维度策略模式(Strategy)状态模式(State)
核心意图封装不同的算法,允许客户端选择和切换封装对象的不同状态及其行为,状态由对象自身决定
切换方式通常由客户端主动切换策略状态切换通常由状态对象或环境类内部决定
策略 / 状态关系策略之间通常无依赖关系状态之间可能存在流转关系(如待支付→已支付)
典型场景支付方式选择、排序算法选择订单状态管理、生命周期管理

七、优点

  1. 消除条件判断:用多态替代if-else或switch语句,使代码更清晰、更易维护;
  2. 增强扩展性:新增算法只需实现策略接口,无需修改现有代码,符合开放 - 封闭原则;
  3. 提高复用性:策略对象可以在不同场景中复用;
  4. 便于测试:每个策略都是独立的类,可以单独进行单元测试;
  5. 动态切换:允许在运行时根据需求灵活切换算法。

八、缺点

  1. 增加类数量:每个算法都需要对应一个策略类,可能导致系统中类的数量增加;
  2. 客户端需了解策略:客户端必须知道所有策略的存在,并理解它们的区别,才能选择合适的策略;
  3. 策略通信成本:如果策略需要环境类的大量信息,可能需要通过参数传递,增加通信成本;
  4. 状态维护问题:策略模式不适合需要维护状态的场景,此时状态模式更合适。

九、适用环境

当系统满足以下条件时,适合使用策略模式:

  1. 系统中存在多种算法,且需要根据不同场景选择使用;
  2. 需要动态切换算法,如根据用户设置或运行时条件改变处理方式;
  3. 算法的实现细节需要隐藏,客户端无需知道具体实现;
  4. 代码中存在复杂的条件判断,且这些判断用于选择不同的处理方式。

典型应用场景:

  • 支付系统(支付宝、微信支付、信用卡等);
  • 排序算法(冒泡排序、快速排序、归并排序等);
  • 折扣计算(会员折扣、节日折扣、促销折扣等);
  • 日志记录(控制台日志、文件日志、数据库日志等);
  • 路由规划(最短路径、最快路径、最经济路径等)。

十、模式扩展

10.1 策略工厂

结合工厂模式,创建策略工厂类来管理策略的创建,降低客户端与具体策略的耦合:

public class PaymentStrategyFactory {
    // 根据支付类型创建对应的策略
    public static PaymentStrategy createStrategy(String type) {
        switch (type) {
            case "alipay":
                return new AliPayStrategy();
            case "wechat":
                return new WechatPayStrategy();
            case "creditcard":
                return new CreditCardStrategy();
            default:
                throw new IllegalArgumentException("不支持的支付方式");
        }
    }
}

客户端使用:

// 无需直接创建策略对象,通过工厂获取
PaymentStrategy strategy = PaymentStrategyFactory.createStrategy("alipay");
context.setStrategy(strategy);

10.2 带缓存的策略

对于创建成本较高的策略,可以添加缓存机制,避免重复创建:

public class CachedPaymentStrategyFactory {
    private static final Map<String, PaymentStrategy> CACHE = new HashMap<>();
    
    public static PaymentStrategy getStrategy(String type) {
        // 先从缓存获取,不存在则创建并缓存
        return CACHE.computeIfAbsent(type, key -> {
            switch (key) {
                case "alipay":
                    return new AliPayStrategy();
                case "wechat":
                    return new WechatPayStrategy();
                default:
                    throw new IllegalArgumentException("不支持的支付方式");
            }
        });
    }
}

10.3 策略链

将多个策略组合成链条,依次执行,适用于需要多步处理的场景:

public class StrategyChain {
    private List<PaymentStrategy> strategies = new ArrayList<>();
    
    // 添加策略到链条
    public void addStrategy(PaymentStrategy strategy) {
        strategies.add(strategy);
    }
    
    // 依次执行所有策略,直到成功
    public boolean executeChain(double amount) {
        for (PaymentStrategy strategy : strategies) {
            if (strategy.pay(amount)) {
                return true;
            }
        }
        return false;
    }
}

十一、模式应用

策略模式在软件开发中应用广泛,典型场景包括:

11.1 Java 标准库

  • java.util.Comparator:比较器接口,不同的比较算法实现了该接口,可用于集合排序;
  • java.util.concurrent.ThreadPoolExecutor:线程池的拒绝策略(RejectedExecutionHandler)使用了策略模式;
  • java.io.FileFilter 和 java.io.FilenameFilter:文件过滤策略接口。

11.2 框架应用

  • Spring Framework:资源访问策略(Resource 接口)、事务管理策略等;
  • Android:动画插值器(Interpolator)、布局管理器(LayoutManager)等;
  • 日志框架:不同的日志输出策略(控制台、文件、数据库)。

11.3 业务场景

  • 电商系统:不同的促销策略(满减、折扣、赠品);
  • 支付系统:不同的支付渠道和支付方式;
  • 导航软件:不同的路线规划策略;
  • 游戏开发:不同的 AI 行为策略。

十二、Android 中的应用

Android 框架和开发中,策略模式的应用非常广泛:

12.1 动画插值器(Interpolator)

Android 动画系统使用策略模式定义不同的动画插值策略:

  • 抽象策略:Interpolator 接口
  • 具体策略:AccelerateInterpolator、DecelerateInterpolator、LinearInterpolator 等
  • 环境类:Animation 类持有 Interpolator 引用
// 使用不同的插值策略
Animation animation = new TranslateAnimation(0, 100, 0, 0);
// 设置加速插值策略
animation.setInterpolator(new AccelerateInterpolator());
// 或者设置减速插值策略
// animation.setInterpolator(new DecelerateInterpolator());
animation.setDuration(1000);
view.startAnimation(animation);

12.2 布局管理器(LayoutManager)

RecyclerView 使用策略模式提供不同的布局策略:

  • 抽象策略:LayoutManager 抽象类
  • 具体策略:LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager
  • 环境类:RecyclerView 持有 LayoutManager 引用
RecyclerView recyclerView = findViewById(R.id.recycler_view);
// 设置线性布局策略
recyclerView.setLayoutManager(new LinearLayoutManager(this));
// 或者设置网格布局策略
// recyclerView.setLayoutManager(new GridLayoutManager(this, 2));

12.3 权限请求策略

在处理 Android 权限请求时,可以使用策略模式定义不同的权限处理策略:

// 抽象策略
public interface PermissionStrategy {
    boolean requestPermission(Activity activity, String permission);
}

// 具体策略:简单请求策略
public class SimplePermissionStrategy implements PermissionStrategy {
    @Override
    public boolean requestPermission(Activity activity, String permission) {
        ActivityCompat.requestPermissions(activity, new String[]{permission}, 100);
        return true;
    }
}

// 具体策略:带解释的请求策略
public class ExplainedPermissionStrategy implements PermissionStrategy {
    @Override
    public boolean requestPermission(Activity activity, String permission) {
        if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
            // 显示权限解释
            showPermissionExplanation(activity);
        }
        ActivityCompat.requestPermissions(activity, new String[]{permission}, 100);
        return true;
    }
    
    private void showPermissionExplanation(Activity activity) {
        // 显示解释对话框
    }
}

十三、代码实现(Java 版)

以 "电商支付系统" 为场景,完整实现策略模式:

13.1 步骤 1:定义抽象策略接口

/**
 * 抽象策略:支付策略接口
 */
public interface PaymentStrategy {
    /**
     * 执行支付
     * @param amount 支付金额
     * @return 支付是否成功
     */
    boolean pay(double amount);
    
    /**
     * 获取支付方式名称
     * @return 支付方式名称
     */
    String getPaymentName();
}

13.2 步骤 2:实现具体策略类

/**
 * 具体策略:支付宝支付
 */
public class AliPayStrategy implements PaymentStrategy {
    private String account;
    private String password;
    
    public AliPayStrategy(String account, String password) {
        this.account = account;
        this.password = password;
    }
    
    @Override
    public boolean pay(double amount) {
        System.out.println("=== 支付宝支付 ===");
        System.out.println("验证账号密码...");
        // 模拟支付过程
        System.out.println("从支付宝账号 " + account + " 支付 " + amount + " 元");
        // 模拟支付成功
        return true;
    }
    
    @Override
    public String getPaymentName() {
        return "支付宝";
    }
}
/**
 * 具体策略:信用卡支付
 */
public class CreditCardStrategy implements PaymentStrategy {
    private String cardNumber;
    private String name;
    private String cvv;
    private String expiryDate;
    
    public CreditCardStrategy(String cardNumber, String name, String cvv, String expiryDate) {
        this.cardNumber = cardNumber;
        this.name = name;
        this.cvv = cvv;
        this.expiryDate = expiryDate;
    }
    
    @Override
    public boolean pay(double amount) {
        System.out.println("=== 信用卡支付 ===");
        System.out.println("验证信用卡信息...");
        // 模拟支付过程
        System.out.println("使用信用卡 " + maskCardNumber(cardNumber) + " 支付 " + amount + " 元");
        // 模拟支付成功
        return true;
    }
    
    // 隐藏卡号中间部分
    private String maskCardNumber(String cardNumber) {
        if (cardNumber.length() <= 4) return cardNumber;
        return cardNumber.substring(0, 4) + " **** **** " + cardNumber.substring(cardNumber.length() - 4);
    }
    
    @Override
    public String getPaymentName() {
        return "信用卡支付";
    }
}
/**
 * 具体策略:微信支付
 */
public class WechatPayStrategy implements PaymentStrategy {
    private String openId;
    
    public WechatPayStrategy(String openId) {
        this.openId = openId;
    }
    
    @Override
    public boolean pay(double amount) {
        System.out.println("=== 微信支付 ===");
        System.out.println("验证OpenID...");
        // 模拟支付过程
        System.out.println("从微信账号 " + openId + " 支付 " + amount + " 元");
        // 模拟支付成功
        return true;
    }
    
    @Override
    public String getPaymentName() {
        return "微信支付";
    }
}

13.3 步骤 3:实现环境类

/**
 * 环境类:支付上下文
 */
public class PaymentContext {
    // 持有支付策略引用
    private PaymentStrategy paymentStrategy;
    
    // 构造方法设置初始策略
    public PaymentContext(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }
    
    // 切换支付策略
    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
        System.out.println("已切换到" + paymentStrategy.getPaymentName());
    }
    
    // 执行支付
    public boolean pay(double amount) {
        if (paymentStrategy == null) {
            System.err.println("未设置支付方式");
            return false;
        }
        System.out.println("准备进行支付,金额:" + amount + "元");
        return paymentStrategy.pay(amount);
    }
    
    // 获取当前支付方式
    public String getCurrentPaymentName() {
        return paymentStrategy != null ? paymentStrategy.getPaymentName() : "未设置";
    }
}

13.4 步骤 4:客户端测试

/**
 * 客户端:测试支付策略模式
 */
public class PaymentTest {
    public static void main(String[] args) {
        // 1. 创建不同的支付策略
        PaymentStrategy aliPay = new AliPayStrategy("user@example.com", "password123");
        PaymentStrategy wechatPay = new WechatPayStrategy("o6_bmjrPTlm6_2sgVt7hMZOPfL2M");
        PaymentStrategy creditCard = new CreditCardStrategy(
            "6222021234567890123", "张三", "123", "12/25");
        
        // 2. 创建支付上下文,初始使用支付宝
        PaymentContext paymentContext = new PaymentContext(aliPay);
        
        // 3. 使用支付宝支付
        System.out.println("=== 第一次支付 ===");
        boolean success = paymentContext.pay(99.99);
        System.out.println("支付" + (success ? "成功" : "失败"));
        
        // 4. 切换到微信支付并支付
        System.out.println("\n=== 第二次支付 ===");
        paymentContext.setPaymentStrategy(wechatPay);
        success = paymentContext.pay(199.50);
        System.out.println("支付" + (success ? "成功" : "失败"));
        
        // 5. 切换到信用卡支付并支付
        System.out.println("\n=== 第三次支付 ===");
        paymentContext.setPaymentStrategy(creditCard);
        success = paymentContext.pay(599.00);
        System.out.println("支付" + (success ? "成功" : "失败"));
    }
}

13.5 运行结果

=== 第一次支付 ===
准备进行支付,金额:99.99元
=== 支付宝支付 ===
验证账号密码...
从支付宝账号 user@example.com 支付 99.99 元
支付成功

=== 第二次支付 ===
已切换到微信支付
准备进行支付,金额:199.5元
=== 微信支付 ===
验证OpenID...
从微信账号 o6_bmjrPTlm6_2sgVt7hMZOPfL2M 支付 199.5 元
支付成功

=== 第三次支付 ===
已切换到信用卡支付
准备进行支付,金额:599.0元
=== 信用卡支付 ===
验证信用卡信息...
使用信用卡 6222 **** **** 0123 支付 599.0 元
支付成功

十四、总结

策略模式通过将算法封装成独立的策略对象,实现了算法的灵活切换和复用,是处理多种相似算法场景的优秀解决方案。

  1. 核心价值:
    • 用多态替代条件判断,使代码更清晰、更易维护;
    • 实现算法的动态切换,提高系统的灵活性;
    • 符合开放 - 封闭原则,便于扩展新算法;
    • 分离算法的定义与使用,降低耦合度。
  2. 使用建议:
    • 当系统中存在 3 个以上相似算法且可能扩展时,优先考虑策略模式;
    • 结合工厂模式使用,降低客户端与具体策略的耦合;
    • 对于简单场景(仅 2-3 种固定算法),不必过度设计使用策略模式;
    • 在 Android 开发中,充分利用系统已有的策略模式实现(如 Interpolator、LayoutManager)。
  3. 局限性:
    • 增加了类的数量,可能提高系统的复杂度;
    • 客户端需要了解所有策略才能选择合适的策略;
    • 不适合处理依赖状态的算法,此时状态模式可能更合适。

总之,策略模式是一种简单而强大的设计模式,它通过封装变化点,使系统更加灵活、可扩展和可维护,是每个开发者都应该掌握的设计模式之一

最近更新:: 2025/10/22 15:36
Contributors: 罗凯文, luokaiwen