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

什么是策略模式?

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

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

现实世界类比

想象你去餐厅吃饭:

  • 策略接口​:点菜这个行为

  • 具体策略​:中餐、西餐、日料等不同菜系

  • 上下文​:餐厅(提供点菜环境)

  • 客户端​:你(根据心情选择今天吃什么)

模式结构

复制代码
classDiagram
    class Strategy {
        <<interface>>
        +executeStrategy() void
    }
    
    class ConcreteStrategyA {
        +executeStrategy() void
    }
    
    class ConcreteStrategyB {
        +executeStrategy() void
    }
    
    class Context {
        -strategy: Strategy
        +setStrategy(Strategy) void
        +execute() void
    }
    
    Strategy <|.. ConcreteStrategyA
    Strategy <|.. ConcreteStrategyB
    Context --> Strategy

代码示例:支付系统设计

问题场景

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

传统实现(不推荐)

复制代码

kotlin

复制

复制代码
class PaymentService {
    fun pay(amount: Double, paymentType: String) {
        when (paymentType) {
            "alipay" -> {
                // 复杂的支付宝支付逻辑
                println("支付宝支付:$amount 元")
                // 几十行代码...
            }
            "wechat" -> {
                // 复杂的微信支付逻辑
                println("微信支付:$amount 元")
                // 几十行代码...
            }
            "bank" -> {
                // 复杂的银行卡支付逻辑
                println("银行卡支付:$amount 元")
                // 几十行代码...
            }
            else -> throw IllegalArgumentException("不支持的支付方式")
        }
    }
}

// 使用示例
val service = PaymentService()
service.pay(100.0, "alipay") // 添加新支付方式需要修改这个类!

问题分析:​

  • ❌ 违反开闭原则:添加新支付方式需要修改现有代码

  • ❌ 职责过重:一个类包含所有支付逻辑

  • ❌ 难以测试:支付逻辑耦合在一起

策略模式实现(推荐)

1. 定义策略接口
复制代码

kotlin

复制

复制代码
interface PaymentStrategy {
    fun pay(amount: Double): Boolean
    fun getStrategyName(): String
}
2. 实现具体策略
复制代码

kotlin

复制

复制代码
// 支付宝支付策略
class AlipayStrategy : PaymentStrategy {
    override fun pay(amount: Double): Boolean {
        println("调用支付宝API,支付金额:$amount 元")
        // 实际的支付宝支付逻辑
        return true // 支付成功
    }
    
    override fun getStrategyName() = "支付宝"
}

// 微信支付策略
class WechatPayStrategy : PaymentStrategy {
    override fun pay(amount: Double): Boolean {
        println("调用微信支付API,支付金额:$amount 元")
        // 实际的微信支付逻辑
        return true
    }
    
    override fun getStrategyName() = "微信支付"
}

// 银行卡支付策略
class BankCardStrategy : PaymentStrategy {
    override fun pay(amount: Double): Boolean {
        println("调用银联API,支付金额:$amount 元")
        // 实际的银行卡支付逻辑
        return true
    }
    
    override fun getStrategyName() = "银行卡"
}
3. 支付上下文
复制代码

kotlin

复制

复制代码
class PaymentContext {
    private var strategy: PaymentStrategy? = null
    
    fun setPaymentStrategy(strategy: PaymentStrategy) {
        this.strategy = strategy
        println("支付方式切换为:${strategy.getStrategyName()}")
    }
    
    fun executePayment(amount: Double): Boolean {
        return strategy?.pay(amount) ?: throw IllegalStateException("请先设置支付策略")
    }
    
    fun getCurrentStrategy() = strategy?.getStrategyName() ?: "未设置"
}
4. 客户端使用
复制代码

kotlin

复制

复制代码
fun main() {
    val context = PaymentContext()
    
    // 用户选择支付宝支付
    context.setPaymentStrategy(AlipayStrategy())
    context.executePayment(100.0)
    
    // 用户切换为微信支付
    context.setPaymentStrategy(WechatPayStrategy())
    context.executePayment(200.0)
    
    // 动态根据条件选择策略
    val userPreference = getUserPaymentPreference() // 模拟获取用户偏好
    val strategy = when (userPreference) {
        "alipay" -> AlipayStrategy()
        "wechat" -> WechatPayStrategy()
        "bank" -> BankCardStrategy()
        else -> AlipayStrategy() // 默认策略
    }
    
    context.setPaymentStrategy(strategy)
    context.executePayment(150.0)
}

fun getUserPaymentPreference() = "wechat" // 模拟数据

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

业务需求

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

  • 普通折扣(固定比例)

  • 满减折扣(满100减20)

  • 会员折扣(根据会员等级)

  • 季节性折扣(特定时间段)

策略模式实现

复制代码

kotlin

复制

复制代码
// 折扣策略接口
interface DiscountStrategy {
    fun calculateDiscount(originalPrice: Double): Double
    fun getStrategyDescription(): String
}

// 具体策略实现
class PercentageDiscountStrategy(private val discountRate: Double) : DiscountStrategy {
    override fun calculateDiscount(originalPrice: Double): Double {
        return originalPrice * discountRate
    }
    
    override fun getStrategyDescription() = "${(discountRate * 100)}% 折扣"
}

class FullReductionStrategy(private val fullAmount: Double, private val reduction: Double) : DiscountStrategy {
    override fun calculateDiscount(originalPrice: Double): Double {
        return if (originalPrice >= fullAmount) reduction else 0.0
    }
    
    override fun getStrategyDescription() = "满 $fullAmount 减 $reduction"
}

class MemberDiscountStrategy(private val memberLevel: String) : DiscountStrategy {
    private val discountRates = mapOf(
        "gold" to 0.8,    // 金卡8折
        "silver" to 0.9,  // 银卡9折
        "normal" to 0.95  // 普通卡95折
    )
    
    override fun calculateDiscount(originalPrice: Double): Double {
        val rate = discountRates[memberLevel] ?: 1.0
        return originalPrice * (1 - rate)
    }
    
    override fun getStrategyDescription() = "${memberLevel}会员折扣"
}

// 折扣上下文
class DiscountContext {
    private var strategy: DiscountStrategy = PercentageDiscountStrategy(1.0) // 默认无折扣
    
    fun setDiscountStrategy(strategy: DiscountStrategy) {
        this.strategy = strategy
    }
    
    fun calculateFinalPrice(originalPrice: Double): Double {
        val discount = strategy.calculateDiscount(originalPrice)
        val finalPrice = originalPrice - discount
        
        println("原价:$originalPrice")
        println("折扣策略:${strategy.getStrategyDescription()}")
        println("折扣金额:$discount")
        println("最终价格:$finalPrice")
        println("---")
        
        return finalPrice
    }
}

// 使用示例
fun main() {
    val context = DiscountContext()
    val originalPrice = 200.0
    
    // 测试不同折扣策略
    context.setDiscountStrategy(PercentageDiscountStrategy(0.8)) // 8折
    context.calculateFinalPrice(originalPrice)
    
    context.setDiscountStrategy(FullReductionStrategy(100.0, 20.0)) // 满100减20
    context.calculateFinalPrice(originalPrice)
    
    context.setDiscountStrategy(MemberDiscountStrategy("gold")) // 金卡会员
    context.calculateFinalPrice(originalPrice)
}

策略模式的优点

✅ 开闭原则

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

复制代码

kotlin

复制

复制代码
// 新增一个节日折扣策略
class FestivalDiscountStrategy : DiscountStrategy {
    override fun calculateDiscount(originalPrice: Double): Double {
        return originalPrice * 0.7 // 节日7折
    }
    
    override fun getStrategyDescription() = "节日特惠折扣"
}

// 使用新策略(无需修改现有代码)
context.setDiscountStrategy(FestivalDiscountStrategy())

✅ 消除条件判断

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

✅ 易于测试

每个策略可以独立测试:

复制代码

kotlin

复制

复制代码
@Test
fun testPercentageDiscount() {
    val strategy = PercentageDiscountStrategy(0.8)
    assertEquals(80.0, strategy.calculateDiscount(100.0))
}

✅ 算法复用

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

适用场景

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

  1. 多种算法变体​:一个功能有多个实现版本

  2. 避免条件爆炸​:复杂的if-else或switch-case语句

  3. 算法需要独立变化​:不同算法可能独立演化和替换

  4. 客户端需要灵活选择​:运行时动态切换算法

🚫 不适用的情况:

  1. 算法很少变化​:如果算法基本固定,过度设计反而复杂

  2. 客户端直接调用简单算法​:如果算法很简单,直接调用即可

  3. 算法间差异很小​:如果算法基本相同,用参数控制更简单

与其他模式的关系

🔄 策略模式 vs 状态模式

  • 策略模式​:客户端主动选择算法

  • 状态模式​:状态自动转换,客户端不感知状态变化

🔄 策略模式 vs 工厂模式

  • 策略模式​:关注算法的选择和替换

  • 工厂模式​:关注对象的创建和初始化

  • 经常结合使用​:工厂创建策略对象

Kotlin 的优化实现

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

使用函数类型

复制代码

kotlin

复制

复制代码
typealias DiscountStrategy = (Double) -> Double

val percentageDiscount: DiscountStrategy = { price -> price * 0.8 }
val fullReductionDiscount: DiscountStrategy = { price -> if (price > 100) price - 20 else price }

class DiscountCalculator(private var strategy: DiscountStrategy = { it }) {
    fun setStrategy(newStrategy: DiscountStrategy) {
        strategy = newStrategy
    }
    
    fun calculate(price: Double) = strategy(price)
}

// 使用
val calculator = DiscountCalculator(percentageDiscount)
println(calculator.calculate(100.0)) // 80.0

使用密封类

复制代码

kotlin

复制

复制代码
sealed class DiscountStrategy {
    object NoDiscount : DiscountStrategy()
    data class Percentage(val rate: Double) : DiscountStrategy()
    data class FullReduction(val full: Double, val reduction: Double) : DiscountStrategy()
    
    fun calculate(price: Double): Double = when (this) {
        is NoDiscount -> price
        is Percentage -> price * rate
        is FullReduction -> if (price >= full) price - reduction else price
    }
}

总结

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

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

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

相关推荐
科研小白_12 小时前
基于遗传算法优化BP神经网络(GA-BP)的数据时序预测
人工智能·算法·回归
Terry Cao 漕河泾12 小时前
基于dtw算法的动作、动态识别
算法
Miraitowa_cheems15 小时前
LeetCode算法日记 - Day 73: 最小路径和、地下城游戏
数据结构·算法·leetcode·职场和发展·深度优先·动态规划·推荐算法
野蛮人6号15 小时前
力扣热题100道之560和位K的子数组
数据结构·算法·leetcode
Swift社区16 小时前
LeetCode 400 - 第 N 位数字
算法·leetcode·职场和发展
fengfuyao98517 小时前
BCH码编译码仿真与误码率性能分析
算法
小白不想白a17 小时前
每日手撕算法--哈希映射/链表存储数求和
数据结构·算法
剪一朵云爱着18 小时前
力扣2080. 区间内查询数字的频率
算法·leetcode
落日漫游18 小时前
数据结构笔试核心考点
java·开发语言·算法