策略模式:让算法选择像点菜一样简单

作者:太过平凡的小蚂蚁日期:2025/10/18

什么是策略模式?

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

简单来说:​定义策略家族,让客户端自由选择。​

现实世界类比

想象你去餐厅吃饭:

  • 策略接口​:点菜这个行为
  • 具体策略​:中餐、西餐、日料等不同菜系
  • 上下文​:餐厅(提供点菜环境)
  • 客户端​:你(根据心情选择今天吃什么)

模式结构

1classDiagram
2    class Strategy {
3        <<interface>>
4        +executeStrategy() void
5    }
6    
7    class ConcreteStrategyA {
8        +executeStrategy() void
9    }
10    
11    class ConcreteStrategyB {
12        +executeStrategy() void
13    }
14    
15    class Context {
16        -strategy: Strategy
17        +setStrategy(Strategy) void
18        +execute() void
19    }
20    
21    Strategy <|.. ConcreteStrategyA
22    Strategy <|.. ConcreteStrategyB
23    Context --> Strategy

代码示例:支付系统设计

问题场景

我们需要实现一个支付系统,支持多种支付方式(支付宝、微信、银行卡),并且未来可能添加新的支付方式。

传统实现(不推荐)

kotlin

复制

1class PaymentService {
2    fun pay(amount: Double, paymentType: String) {
3        when (paymentType) {
4            "alipay" -> {
5                // 复杂的支付宝支付逻辑
6                println("支付宝支付:$amount 元")
7                // 几十行代码...
8            }
9            "wechat" -> {
10                // 复杂的微信支付逻辑
11                println("微信支付:$amount 元")
12                // 几十行代码...
13            }
14            "bank" -> {
15                // 复杂的银行卡支付逻辑
16                println("银行卡支付:$amount 元")
17                // 几十行代码...
18            }
19            else -> throw IllegalArgumentException("不支持的支付方式")
20        }
21    }
22}
23
24// 使用示例
25val service = PaymentService()
26service.pay(100.0, "alipay") // 添加新支付方式需要修改这个类!

问题分析:​

  • ❌ 违反开闭原则:添加新支付方式需要修改现有代码
  • ❌ 职责过重:一个类包含所有支付逻辑
  • ❌ 难以测试:支付逻辑耦合在一起

策略模式实现(推荐)

1. 定义策略接口

kotlin

复制

1interface PaymentStrategy {
2    fun pay(amount: Double): Boolean
3    fun getStrategyName(): String
4}
2. 实现具体策略

kotlin

复制

1// 支付宝支付策略
2class AlipayStrategy : PaymentStrategy {
3    override fun pay(amount: Double): Boolean {
4        println("调用支付宝API,支付金额:$amount 元")
5        // 实际的支付宝支付逻辑
6        return true // 支付成功
7    }
8    
9    override fun getStrategyName() = "支付宝"
10}
11
12// 微信支付策略
13class WechatPayStrategy : PaymentStrategy {
14    override fun pay(amount: Double): Boolean {
15        println("调用微信支付API,支付金额:$amount 元")
16        // 实际的微信支付逻辑
17        return true
18    }
19    
20    override fun getStrategyName() = "微信支付"
21}
22
23// 银行卡支付策略
24class BankCardStrategy : PaymentStrategy {
25    override fun pay(amount: Double): Boolean {
26        println("调用银联API,支付金额:$amount 元")
27        // 实际的银行卡支付逻辑
28        return true
29    }
30    
31    override fun getStrategyName() = "银行卡"
32}
3. 支付上下文

kotlin

复制

1class PaymentContext {
2    private var strategy: PaymentStrategy? = null
3    
4    fun setPaymentStrategy(strategy: PaymentStrategy) {
5        this.strategy = strategy
6        println("支付方式切换为:${strategy.getStrategyName()}")
7    }
8    
9    fun executePayment(amount: Double): Boolean {
10        return strategy?.pay(amount) ?: throw IllegalStateException("请先设置支付策略")
11    }
12    
13    fun getCurrentStrategy() = strategy?.getStrategyName() ?: "未设置"
14}
4. 客户端使用

kotlin

复制

1fun main() {
2    val context = PaymentContext()
3    
4    // 用户选择支付宝支付
5    context.setPaymentStrategy(AlipayStrategy())
6    context.executePayment(100.0)
7    
8    // 用户切换为微信支付
9    context.setPaymentStrategy(WechatPayStrategy())
10    context.executePayment(200.0)
11    
12    // 动态根据条件选择策略
13    val userPreference = getUserPaymentPreference() // 模拟获取用户偏好
14    val strategy = when (userPreference) {
15        "alipay" -> AlipayStrategy()
16        "wechat" -> WechatPayStrategy()
17        "bank" -> BankCardStrategy()
18        else -> AlipayStrategy() // 默认策略
19    }
20    
21    context.setPaymentStrategy(strategy)
22    context.executePayment(150.0)
23}
24
25fun getUserPaymentPreference() = "wechat" // 模拟数据

更复杂的实战案例:折扣策略系统

业务需求

电商平台需要支持多种折扣策略:

  • 普通折扣(固定比例)
  • 满减折扣(满100减20)
  • 会员折扣(根据会员等级)
  • 季节性折扣(特定时间段)

策略模式实现

kotlin

复制

1// 折扣策略接口
2interface DiscountStrategy {
3    fun calculateDiscount(originalPrice: Double): Double
4    fun getStrategyDescription(): String
5}
6
7// 具体策略实现
8class PercentageDiscountStrategy(private val discountRate: Double) : DiscountStrategy {
9    override fun calculateDiscount(originalPrice: Double): Double {
10        return originalPrice * discountRate
11    }
12    
13    override fun getStrategyDescription() = "${(discountRate * 100)}% 折扣"
14}
15
16class FullReductionStrategy(private val fullAmount: Double, private val reduction: Double) : DiscountStrategy {
17    override fun calculateDiscount(originalPrice: Double): Double {
18        return if (originalPrice >= fullAmount) reduction else 0.0
19    }
20    
21    override fun getStrategyDescription() = "满 $fullAmount 减 $reduction"
22}
23
24class MemberDiscountStrategy(private val memberLevel: String) : DiscountStrategy {
25    private val discountRates = mapOf(
26        "gold" to 0.8,    // 金卡8折
27        "silver" to 0.9,  // 银卡9折
28        "normal" to 0.95  // 普通卡95折
29    )
30    
31    override fun calculateDiscount(originalPrice: Double): Double {
32        val rate = discountRates[memberLevel] ?: 1.0
33        return originalPrice * (1 - rate)
34    }
35    
36    override fun getStrategyDescription() = "${memberLevel}会员折扣"
37}
38
39// 折扣上下文
40class DiscountContext {
41    private var strategy: DiscountStrategy = PercentageDiscountStrategy(1.0) // 默认无折扣
42    
43    fun setDiscountStrategy(strategy: DiscountStrategy) {
44        this.strategy = strategy
45    }
46    
47    fun calculateFinalPrice(originalPrice: Double): Double {
48        val discount = strategy.calculateDiscount(originalPrice)
49        val finalPrice = originalPrice - discount
50        
51        println("原价:$originalPrice")
52        println("折扣策略:${strategy.getStrategyDescription()}")
53        println("折扣金额:$discount")
54        println("最终价格:$finalPrice")
55        println("---")
56        
57        return finalPrice
58    }
59}
60
61// 使用示例
62fun main() {
63    val context = DiscountContext()
64    val originalPrice = 200.0
65    
66    // 测试不同折扣策略
67    context.setDiscountStrategy(PercentageDiscountStrategy(0.8)) // 8折
68    context.calculateFinalPrice(originalPrice)
69    
70    context.setDiscountStrategy(FullReductionStrategy(100.0, 20.0)) // 满100减20
71    context.calculateFinalPrice(originalPrice)
72    
73    context.setDiscountStrategy(MemberDiscountStrategy("gold")) // 金卡会员
74    context.calculateFinalPrice(originalPrice)
75}

策略模式的优点

✅ 开闭原则

添加新策略无需修改现有代码:

kotlin

复制

1// 新增一个节日折扣策略
2class FestivalDiscountStrategy : DiscountStrategy {
3    override fun calculateDiscount(originalPrice: Double): Double {
4        return originalPrice * 0.7 // 节日7折
5    }
6    
7    override fun getStrategyDescription() = "节日特惠折扣"
8}
9
10// 使用新策略(无需修改现有代码)
11context.setDiscountStrategy(FestivalDiscountStrategy())

✅ 消除条件判断

用多态替代复杂的条件语句,代码更清晰。

✅ 易于测试

每个策略可以独立测试:

kotlin

复制

1@Test
2fun testPercentageDiscount() {
3    val strategy = PercentageDiscountStrategy(0.8)
4    assertEquals(80.0, strategy.calculateDiscount(100.0))
5}

✅ 算法复用

策略可以在不同上下文中复用。

适用场景

🎯 推荐使用策略模式的情况:

  1. 多种算法变体​:一个功能有多个实现版本
  2. 避免条件爆炸​:复杂的if-else或switch-case语句
  3. 算法需要独立变化​:不同算法可能独立演化和替换
  4. 客户端需要灵活选择​:运行时动态切换算法

🚫 不适用的情况:

  1. 算法很少变化​:如果算法基本固定,过度设计反而复杂
  2. 客户端直接调用简单算法​:如果算法很简单,直接调用即可
  3. 算法间差异很小​:如果算法基本相同,用参数控制更简单

与其他模式的关系

🔄 策略模式 vs 状态模式

  • 策略模式​:客户端主动选择算法
  • 状态模式​:状态自动转换,客户端不感知状态变化

🔄 策略模式 vs 工厂模式

  • 策略模式​:关注算法的选择和替换
  • 工厂模式​:关注对象的创建和初始化
  • 经常结合使用​:工厂创建策略对象

Kotlin 的优化实现

利用 Kotlin 的语言特性,可以写出更简洁的策略模式:

使用函数类型

kotlin

复制

1typealias DiscountStrategy = (Double) -> Double
2
3val percentageDiscount: DiscountStrategy = { price -> price * 0.8 }
4val fullReductionDiscount: DiscountStrategy = { price -> if (price > 100) price - 20 else price }
5
6class DiscountCalculator(private var strategy: DiscountStrategy = { it }) {
7    fun setStrategy(newStrategy: DiscountStrategy) {
8        strategy = newStrategy
9    }
10    
11    fun calculate(price: Double) = strategy(price)
12}
13
14// 使用
15val calculator = DiscountCalculator(percentageDiscount)
16println(calculator.calculate(100.0)) // 80.0

使用密封类

kotlin

复制

1sealed class DiscountStrategy {
2    object NoDiscount : DiscountStrategy()
3    data class Percentage(val rate: Double) : DiscountStrategy()
4    data class FullReduction(val full: Double, val reduction: Double) : DiscountStrategy()
5    
6    fun calculate(price: Double): Double = when (this) {
7        is NoDiscount -> price
8        is Percentage -> price * rate
9        is FullReduction -> if (price >= full) price - reduction else price
10    }
11}

总结

策略模式是应对算法变化的利器,它通过"封装变化"让系统更灵活。记住策略模式的核心思想:

找出代码中可能变化的部分,把它抽离出来,封装成可互换的策略。​

在实际项目中,当您发现复杂的条件判断或经常需要添加新的算法变体时,就是使用策略模式的最佳时机。


策略模式:让算法选择像点菜一样简单》 是转载文章,点击查看原文


相关推荐


VSCODE GDB调试
Gary Studio2025/10/16

流程 1.首先点击左侧的小虫子按钮 2.选择gdb调试 3.在program一栏填上你需要调试的可执行文件 注意:编译的时候记得在编译命令的最后或者在前面的语句中添加-g 增加调试的选项 例如下面 PS D:\CODE\c> gcc .\04_example2.c -o .\04_example2 -g 4.在需要调试的代码打上断点即可开启运行。 补充 有时候运行不起来有可能是没有安装gdb或者是gdb的环境变量没有配置造成的 验证gdb是否安装 输入以下指令


SpringBoot实现JWT动态密钥轮换
风象南2025/10/15

背景:为什么 JWT 密钥也要"轮换" JWT(JSON Web Token) 是当代认证体系的常用方案, 无论是单体系统、微服务、还是前后端分离登录,几乎都会用到它。 但在大多数系统里,签名密钥往往是一成不变的—— 一旦生成,常年不换,代码里写死或放在配置文件中。 这其实非常危险: 一旦密钥被误传或泄露,攻击者就能伪造任意用户的合法 Token 无论是测试环境误配置,还是日志误打出 key,都可能导致密钥泄露,带来安全隐患 于是我们面临一个工程问题: "如何能动态更新 JWT 签名密钥,


从Java集合到云原生现代数据管理的演进之路
kkjt01302025/10/14

从Java集合到云原生现代数据管理的演进之路在软件开发的漫长历程中,数据管理技术的演进一直是推动应用架构变革的核心动力。从最初在单一应用内存中操作的Java集合,到今天跨越全球分布式环境的云原生数据平台,这条路不仅反映了技术的飞跃,更体现了我们对数据处理能力、规模和敏捷性不断增长的需求。这一演进是渐进而深刻的,每一步都为了解决特定时代的核心挑战。Java集合:单机时代的基石Java集合框架(Java Collections Framework, JCF)是早期Java应用数据管理的基石。它以Li


如何为Spring Cloud Gateway配置具体的负载均衡策略?
IT橘子皮2025/10/12

以下是针对 ​Spring Cloud Gateway​ 配置具体负载均衡策略(如加权轮询、最小连接数等)的详细指南,结合最新技术文档和最佳实践: 一、基础配置与依赖 1. ​引入依赖​ 确保项目中已包含 Spring Cloud Gateway 和负载均衡器(Spring Cloud LoadBalancer)的依赖: <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cl


零基础学JavaScript:手把手带你搭建环境,写出第一个程序!
良山有风来2025/10/11

开头:你是不是也遇到过这些问题? 刚学JavaScript的时候,你是不是一脸懵? 打开教程,满屏的“Node.js”、“npm”、“VS Code”,完全不知道从哪下手? 照着网上的教程配置环境,结果各种报错,心态爆炸? 写了半天代码,连个“Hello World”都显示不出来? 别担心!这篇文章就是为你准备的。 我会用最直白的方式,带你一步步搭建JavaScript开发环境,并写出你的第一个程序。 看完这篇文章,你不仅能顺利运行第一个JavaScript程序,还能理解背后的原理,为后续学习打


【机器学习】无监督学习 —— K-Means 聚类、DBSCAN 聚类
一杯水果茶!2025/10/9

K-Means 聚类标准 K-Means 算法K-Means 评估:肘部法则(Elbow Method)与轮廓系数(Silhouette Score)1. 肘部法则(Elbow Method)2. 轮廓系数(Silhouette Score) DBSCAN 聚类(Density-Based Spatial Clustering of Applications with Noise)DBSCAN 的关键参数DBSCAN 算法 K-Means 聚类 K‑Means 聚类 是一种


一文读懂 Vue 组件间通信机制(含 Vue2 / Vue3 区别)
excel2025/10/8

一、组件间通信的概念 在 Vue 中,组件(Component) 是最核心的概念之一。每个 .vue 文件都可以视为一个独立的组件。 而 通信(Communication) 是指一个组件如何将信息传递给另一个组件。 通俗地说: 组件间通信,就是不同组件之间如何共享数据、触发行为、进行信息交互的过程。 例如:当我们使用 UI 框架中的 table 组件时,需要向它传入 data 数据,这个“传值”的过程本质上就是一种组件通信。 二、组件间通信解决了什么问题? 在实际开发中,每个组件都有自己的


【Node】Node.js 多进程与多线程:Cluster 与 Worker Threads 入门
你的人类朋友2025/10/6

前言 在 Node.js 开发中,处理 CPU 密集型任务和提升应用性能是常见需求。 今天我们来深入理解 Node.js 提供的两种并发处理方案:Cluster 模块和 Worker Threads 模块。 ☺️ 这边要求阅读本文的新手小伙伴要有一个印象:【Cluster】 与【进程】相关,【Worker Threads】 与【线程】相关 小贴士 📚: ✨Cluster 的中文意思是集群 ✨Worker Threads 的中文意思是工作线程 官方定义解析 Node.js 官方文档指出:


【Linux系统】快速入门一些常用的基础指令
落羽的落羽2025/10/5

各位大佬好,我是落羽!一个坚持不断学习进步的学生。 如果您觉得我的文章还不错,欢迎多多互三分享交流,一起学习进步! 也欢迎关注我的blog主页: 落羽的落羽 文章目录 pwd、whoami、clearmkdir、touch、treecdlsrmdir、rmmanechocat、tac、more、less、head、tailcp、mvfind、which、whereisaliasgrepzip、unzip、taruname 开始学习使用Linux,我们首先要掌握一些Linux


快速搭建redis环境并使用redis客户端进行连接测试
你的人类朋友2025/10/4

前言 最近工作要用到 redis,所以这边简要记录一下自己搭建 redis 环境的过程,后面忘记了回头看比较方便。 正文 一、环境安装 这边推荐个跨 windows 和 mac 的 redis 客户端,another Redis Desktop Manager windows 一般安装Another-Redis-Desktop-Manager-win-1.7.1-x64.exe mac 一般安装Another-Redis-Desktop-Manager-mac-1.7.1-arm64.dmg 具

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0