设计模式学习(24) 23-22 策略模式

文章目录

  • [0. 个人感悟](#0. 个人感悟)
  • [1. 概念](#1. 概念)
  • [2. 适配场景](#2. 适配场景)
    • [2.1 适合的场景](#2.1 适合的场景)
    • [2.2 常见场景举例](#2.2 常见场景举例)
  • [3. 实现方法](#3. 实现方法)
    • [3.1 实现思路](#3.1 实现思路)
    • [3.2 UML类图](#3.2 UML类图)
    • [3.3 代码示例](#3.3 代码示例)
  • [4. 优缺点](#4. 优缺点)
    • [4.1 优点](#4.1 优点)
    • [4.2 缺点](#4.2 缺点)
  • [5. 源码分析](#5. 源码分析)
    • [5.1 Java Collections框架中的Comparator](#5.1 Java Collections框架中的Comparator)

0. 个人感悟

  • 策略模式是很典型的设计模式,非常能突出编程思想:面向接口编程、隔离变化与不变、封装实现
  • 实际使用场景挺多,比如不同的支付策略、运维策略等
  • 策略模式和状态模式类图很像,但有着本质区别,使用场景不同
  • 当策略较多时,建议结合简单工厂模式,抽取策略工厂,将策略选取逻辑放到工厂,有利于职责单一

1. 概念

英文定义 (《设计模式:可复用面向对象软件的基础》)

Define a family of algorithms, encapsulate each one, and make them interchangealbe. Strategy lets the algorithm vary independently form clients that use it.

中文翻译

定义一系列算法,将每一个算法封装起来,并使它们可以相互替换。策略模式让算法独立于使用它的客户而变化。

理解

  • 将算法封装成独立的策略类,而不是将算法硬编码在客户端中
  • 客户端可以在运行时灵活选择不同的算法实现
  • 策略类实现相同的接口,可以无缝替换
  • 消除了大量的条件判断语句,使代码更清晰
  • 符合开闭原则,增加新策略无需修改现有代码

2. 适配场景

2.1 适合的场景

  1. 多种算法实现同一功能:当一个任务可以通过多种算法完成,且这些算法可能在不同场景下需要切换时
  2. 需要消除条件分支:当代码中存在大量if-else或switch-case语句来选择不同算法时
  3. 算法独立变化:当算法的实现可能会独立于使用它的客户端而变化时
  4. 运行时决策:当需要在运行时决定使用哪种算法时

2.2 常见场景举例

  1. 支付系统:多种支付方式(信用卡、PayPal、微信、支付宝)
  2. 排序算法:不同排序策略(快速排序、归并排序、堆排序)
  3. 压缩算法:不同压缩格式(ZIP、RAR、7Z、GZIP)
  4. 导航系统:不同路径规划策略(最短路径、最少时间、避开高速)
  5. 验证系统:不同验证方式(邮箱、手机、人脸识别)
  6. 折扣策略:不同促销活动(满减、折扣、返现)
  7. 数据导出:不同格式导出(CSV、PDF、Excel、JSON)

3. 实现方法

3.1 实现思路

  1. 识别系统中可能变化的部分,将其抽象为策略接口
  2. 为每个具体算法创建实现策略接口的类
  3. 创建上下文类,持有策略接口的引用
  4. 在上下文类中提供设置策略的方法
  5. 客户端根据需要创建具体策略对象并设置给上下文
  6. 上下文通过策略接口调用具体算法,而不知道具体实现细节

3.2 UML类图

角色说明

  • Context(上下文):持有一个Strategy对象的引用,并通过该接口调用具体算法
  • Strategy(策略接口):定义所有支持的算法的公共接口
  • ConcreteStrategy(具体策略):实现Strategy接口的具体算法类

3.3 代码示例

背景

已购物支付为例

  • 简化业务逻辑,支付策略可以是支付宝、微信,支持切换
    策略接口和实现类
java 复制代码
public interface PaymentStrategy {  
    /**  
     * @return designpattern.strategy.PayType 支付类型  
     * @description 获取支付类型,策略key  
     * @author bigHao  
     * @date 2026/1/28  
     **/    
     PayType getPayType();  
  
    /**  
     * @param amount 金额  
     * @description 支付  
     * @author bigHao  
     * @date 2026/1/28  
     **/    
     void pay(double amount);  
}

public class PayPalStrategy implements PaymentStrategy {  
    private String email;  
  
    public PayPalStrategy(String email) {  
        this.email = email;  
    }  
  
    @Override  
    public PayType getPayType() {  
        return PayType.PAY_PAL;  
    }  
  
    @Override  
    public void pay(double amount) {  
        System.out.println(STR."pay \{amount} using PayPal with email \{email}");  
    }  
}

public class WeChatPayStrategy implements PaymentStrategy {  
    private String weChatId;  
  
    public WeChatPayStrategy(String weChatId) {  
        this.weChatId = weChatId;  
    }  
  
    @Override  
    public PayType getPayType() {  
        return PayType.WECHAT_PAY;  
    }  
  
    @Override  
    public void pay(double amount) {  
        System.out.println(STR."pay \{amount} using WeChatPay with id \{weChatId}");  
    }  
}

策略工厂

java 复制代码
public class PayStrategyFactory {  
    public static Map<PayType, PaymentStrategy> map = new HashMap<PayType, PaymentStrategy>();  
  
    static {  
        PaymentStrategy aliPay = new PayPalStrategy("11111@ali.com");  
        PaymentStrategy weChatPay = new WeChatPayStrategy("weChatId001");  
        map.put(aliPay.getPayType(), aliPay);  
        map.put(weChatPay.getPayType(), weChatPay);  
    }  
  
  
    /**  
     * @param payType  
     * @return designpattern.strategy.PaymentStrategy 支付策略  
     * @description 获取策略  
     * @author bigHao  
     * @date 2026/1/28  
     **/    
     public static PaymentStrategy getPaymentStrategy(PayType payType) {  
        return map.get(payType);  
    }  
}

上下文

java 复制代码
public class ShoppingCart {  
    private double amount;  
  
    private PaymentStrategy strategy;  
  
    /**  
     * @description 结账  
     * @author bigHao  
     * @date 2026/1/28  
     **/    
     public void checkOut() {  
        strategy.pay(amount);  
    }  
  
    public double getAmount() {  
        return amount;  
    }  
  
    public void setAmount(double amount) {  
        this.amount = amount;  
    }  
  
    public PaymentStrategy getStrategy() {  
        return strategy;  
    }  
  
    public void setStrategy(PaymentStrategy strategy) {  
        this.strategy = strategy;  
    }  
}

辅助类

java 复制代码
public enum PayType {  
    PAY_PAL, // 支付宝  
    WECHAT_PAY // 微信  
}

测试

java 复制代码
public class Client {  
    static void main() {  
        // 初始化  
        ShoppingCart cart = new ShoppingCart();  
        cart.setAmount(100);  
  
        // 支付宝  
        cart.setStrategy(PayStrategyFactory.getPaymentStrategy(PayType.PAY_PAL));  
        cart.checkOut();  
  
        // 切换到微信  
        cart.setStrategy(PayStrategyFactory.getPaymentStrategy(PayType.WECHAT_PAY));  
        cart.checkOut();  
    }  
}

输出

复制代码
pay 100.0 using PayPal with email 11111@ali.com
pay 100.0 using WeChatPay with id weChatId001

4. 优缺点

4.1 优点

  1. 高内聚低耦合

    • 算法实现与使用分离,提高了内聚性
    • 上下文与具体策略解耦,降低了耦合度
  2. 开闭原则

    • 易于扩展新策略,无需修改现有代码
    • 支持在不修改上下文的情况下增加新算法
  3. 可读性

    • 消除了复杂的条件判断语句
    • 每个策略类职责单一,代码清晰
  4. 可维护性

    • 算法变化独立,修改一个策略不影响其他
    • 便于单元测试,每个策略可单独测试
  5. 复用性

    • 策略类可以在不同的上下文环境中复用
    • 避免了代码重复

4.2 缺点

  1. 策略类数量增加

    • 每个算法一个类,可能导致类数量膨胀
    • 需要权衡策略划分的粒度
  2. 客户端必须了解策略差异

    • 客户端需要知道不同策略的区别和适用场景
    • 增加了客户端的复杂性
  3. 对象创建开销

    • 频繁创建和销毁策略对象可能带来性能开销
    • 可考虑使用对象池或享元模式优化
  4. 策略间通信困难

    • 策略间相互独立,难以共享数据
    • 需要额外的通信机制

5. 源码分析

5.1 Java Collections框架中的Comparator

代码流程

java 复制代码
// java.util.Comparator接口就是策略模式的典型应用
public interface Comparator<T> {
    int compare(T o1, T o2);
}

// 具体策略:字符串长度比较器
public class StringLengthComparator implements Comparator<String> {
    @Override
    public int compare(String s1, String s2) {
        return Integer.compare(s1.length(), s2.length());
    }
}

// 上下文:Collections.sort()方法
public static <T> void sort(List<T> list, Comparator<? super T> c) {
    list.sort(c);
}

// 使用示例
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Collections.sort(names, new StringLengthComparator());

角色分析*

  • Strategy接口:Comparator
  • 具体策略:各种实现了Comparator的类,如StringLengthComparator
  • Context:Collections.sort()方法或List.sort()方法
  • 客户端:调用排序方法的代码

参考:

相关推荐
西岸行者3 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意3 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码3 天前
嵌入式学习路线
学习
毛小茛3 天前
计算机系统概论——校验码
学习
babe小鑫3 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms3 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下3 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。3 天前
2026.2.25监控学习
学习
im_AMBER3 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J3 天前
从“Hello World“ 开始 C++
c语言·c++·学习