【西瓜带你学设计模式 | 第十五期 - 策略模式】策略模式 —— 算法封装与动态替换实现、优缺点与适用场景

文章目录

    • 前言
    • [1. 策略模式是什么?](#1. 策略模式是什么?)
    • [2. 策略模式解决什么问题?](#2. 策略模式解决什么问题?)
    • [3. 核心结构](#3. 核心结构)
      • [3.1 Strategy(抽象策略)](#3.1 Strategy(抽象策略))
      • [3.2 ConcreteStrategy(具体策略)](#3.2 ConcreteStrategy(具体策略))
      • [3.3 Context(上下文)](#3.3 Context(上下文))
    • [4. 实现思路](#4. 实现思路)
    • [5. 示例](#5. 示例)
      • [5.1 Strategy:抽象策略](#5.1 Strategy:抽象策略)
      • [5.2 ConcreteStrategy:具体策略](#5.2 ConcreteStrategy:具体策略)
        • [5.2.1 满减策略](#5.2.1 满减策略)
        • [5.2.2 会员折扣策略](#5.2.2 会员折扣策略)
        • [5.2.3 阶梯价策略](#5.2.3 阶梯价策略)
      • [5.3 Context:上下文](#5.3 Context:上下文)
      • [5.4 Client:客户端选择策略并调用](#5.4 Client:客户端选择策略并调用)
    • [6. 优缺点](#6. 优缺点)
      • [6.1 优点](#6.1 优点)
      • [6.2 缺点](#6.2 缺点)
    • [7. 和其他模式怎么区分?](#7. 和其他模式怎么区分?)
      • [7.1 策略 vs 简单工厂/工厂方法](#7.1 策略 vs 简单工厂/工厂方法)
      • [7.2 策略 vs 模板方法](#7.2 策略 vs 模板方法)
      • [7.3 策略 vs 状态模式](#7.3 策略 vs 状态模式)
    • [8. 适用场景](#8. 适用场景)
    • [9. 总结](#9. 总结)

前言

在很多业务系统里,我们经常会遇到"同一类事情,不同情况下用不同算法/规则"的需求,比如:

  • 支付方式不同(支付宝/微信/银行卡/积分)
  • 价格计算不同(满减、折扣、阶梯价、会员价)
  • 消息发送不同(短信/邮件/推送)
  • 文件处理不同(压缩算法/加密算法/校验算法)

这些场景通常有一个共同点:算法会变,但调用方希望保持稳定

策略模式(Strategy Pattern)要解决的核心就是:

把算法/规则从使用它的代码中剥离出来,让它们以"可替换"的方式独立变化;客户端只依赖统一的抽象接口。


1. 策略模式是什么?

策略模式是一种行为型设计模式,通过"封装一组可复用的算法",并让它们都实现同一个接口,从而做到:

  • 客户端根据条件选择合适的策略
  • 后续新增/修改策略时,不需要改动客户端的大结构(遵循开闭原则)

2. 策略模式解决什么问题?

  1. 同一业务流程里会出现多个分支(if/else 很多)
  2. 算法(或规则)经常变化,且变化频率高
  3. 不希望改动调用方逻辑,只希望替换不同实现
  4. 希望代码结构更清晰、可扩展

如果代码里出现"根据类型/条件选择不同计算方式"的分支,而且会不断增多,就很适合策略模式。


3. 核心结构

3.1 Strategy(抽象策略)

定义策略必须实现的方法,例如 calculatePrice(...)

3.2 ConcreteStrategy(具体策略)

多个具体类实现同一接口,对应不同算法或规则。

3.3 Context(上下文)

上下文类持有 Strategy,负责"在合适时机调用策略"。


4. 实现思路

按你给的享元模式示例那种"拆角色 + 实现 + 示例"的思路,策略模式通常是:

  1. 定义 Strategy 接口(统一方法签名)
  2. 写多个 ConcreteStrategy(不同算法)
  3. Context,构造时注入策略,或运行时切换
  4. 客户端只跟 ContextStrategy 交互,不关心具体算法细节

5. 示例

不同规则计算价格

5.1 Strategy:抽象策略

java 复制代码
public interface PricingStrategy {
    double calculate(double originalPrice);
}

5.2 ConcreteStrategy:具体策略

5.2.1 满减策略
java 复制代码
public class DiscountByCouponStrategy implements PricingStrategy {
    @Override
    public double calculate(double originalPrice) {
        // 假设满100减20
        if (originalPrice >= 100) {
            return originalPrice - 20;
        }
        return originalPrice;
    }
}
5.2.2 会员折扣策略
java 复制代码
public class MemberDiscountStrategy implements PricingStrategy {
    @Override
    public double calculate(double originalPrice) {
        // 会员9折
        return originalPrice * 0.9;
    }
}
5.2.3 阶梯价策略
java 复制代码
public class TieredPricingStrategy implements PricingStrategy {
    @Override
    public double calculate(double originalPrice) {
        // 假设阶梯:>=200打8折,>=100打9折,否则原价
        if (originalPrice >= 200) return originalPrice * 0.8;
        if (originalPrice >= 100) return originalPrice * 0.9;
        return originalPrice;
    }
}

5.3 Context:上下文

java 复制代码
public class ShoppingCart {
    private final PricingStrategy strategy;

    public ShoppingCart(PricingStrategy strategy) {
        this.strategy = strategy;
    }

    public double checkout(double originalPrice) {
        return strategy.calculate(originalPrice);
    }
}

5.4 Client:客户端选择策略并调用

java 复制代码
public class Client {
    public static void main(String[] args) {
        double price = 150;

        ShoppingCart cart1 = new ShoppingCart(new DiscountByCouponStrategy());
        System.out.println("券后价格: " + cart1.checkout(price));

        ShoppingCart cart2 = new ShoppingCart(new MemberDiscountStrategy());
        System.out.println("会员价: " + cart2.checkout(price));

        ShoppingCart cart3 = new ShoppingCart(new TieredPricingStrategy());
        System.out.println("阶梯价: " + cart3.checkout(price));
    }
}

会发现:

  • "选择哪种计算方式"的逻辑被独立出来了(策略类)
  • ShoppingCart 不关心具体算法,只负责调用策略
  • 将来新增"新计算规则",只要新增一个策略类即可

6. 优缺点

6.1 优点

  1. 消除大量 if/else(条件分支被策略类替代)
  2. 算法可扩展:新增策略不必大改原代码
  3. 复用性强:策略实现可以在多个上下文中使用
  4. 更符合开闭原则:对扩展开放,对修改关闭

6.2 缺点

  1. 策略类增多:可能导致类数量上升
  2. 客户端/工厂需要维护"策略选择逻辑"(如果选择逻辑仍写一堆 if,可能又回来了)
  3. 如果策略之间共享复杂公共逻辑,可能需要进一步抽象(或结合模板方法/组合等)

7. 和其他模式怎么区分?

7.1 策略 vs 简单工厂/工厂方法

  • 策略:解决"算法/规则如何替换"
  • 工厂:解决"对象如何创建(选择具体策略类)"

实际项目里经常组合使用:工厂负责创建对应策略,策略负责算法本体。

7.2 策略 vs 模板方法

  • 模板方法:把"流程骨架"固定,把某些步骤延迟到子类实现
  • 策略:把"某个可变点"直接封装为独立算法,运行时可切换

7.3 策略 vs 状态模式

  • 状态模式 :对象在不同状态下行为不同,且状态通常会在运行过程中自动切换
  • 策略模式:通常由外部决定当前使用哪种策略(策略本身不一定管理"状态流转")

8. 适用场景

满足以下情况时,策略模式很合适:

  • 系统需要在多种算法/规则之间切换
  • 算法实现彼此差异较大,但对外暴露统一接口
  • 频繁出现"if/else 根据条件选择不同处理"
  • 希望将算法从业务流程中解耦,便于扩展与测试

9. 总结

策略模式通过"抽象策略 + 多个具体策略 + 上下文持有策略引用",把可变算法/规则从调用方逻辑中解耦出来。客户端只需选择合适的策略即可完成行为替换,从而减少分支代码、提高扩展性。

相关推荐
Leinwin2 小时前
GitHub Copilot CLI现已支持连接本地Ollama/vLLM模型(附配置指南)
后端·python·flask
珍朱(珠)奶茶2 小时前
Spring Boot3整合FreeMark、itextpdf 5/7 实现pdf文件导出及注意问题
java·spring boot·后端·pdf·itextpdf
大数据新鸟2 小时前
微服务之Spring Cloud LoadBalancer
java·spring cloud·微服务
杜子不疼.2 小时前
AI Agent 智能体开发入门:AutoGen 多智能体协作实战教程
java·人工智能·spring
樽酒ﻬق2 小时前
构筑容器化基石:Docker 稳定版本抉择、极速安装与配置全解
java·docker·运维开发
星辰_mya2 小时前
深度全面学习负载均衡Ribbon/Spring Cloud LoadBalancer
后端·spring cloud·面试·负载均衡·架构师
weisian1512 小时前
Java并发编程--29-分布式ID的6种方案:从单机到分库分表的“身份证”设计
java·分布式·雪花算法·美团leaf·百度uid
美式请加冰2 小时前
最短路径问题
java·数据结构·算法
小江的记录本2 小时前
【JEECG Boot】 JEECG Boot 数据字典管理——六大核心功能(内含:《JEECG Boot 数据字典开发速查清单》)
java·前端·数据库·spring boot·后端·spring·mybatis