什么是策略模式?
策略模式(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))
}
✅ 算法复用
策略可以在不同上下文中复用。
适用场景
🎯 推荐使用策略模式的情况:
-
多种算法变体:一个功能有多个实现版本
-
避免条件爆炸:复杂的if-else或switch-case语句
-
算法需要独立变化:不同算法可能独立演化和替换
-
客户端需要灵活选择:运行时动态切换算法
🚫 不适用的情况:
-
算法很少变化:如果算法基本固定,过度设计反而复杂
-
客户端直接调用简单算法:如果算法很简单,直接调用即可
-
算法间差异很小:如果算法基本相同,用参数控制更简单
与其他模式的关系
🔄 策略模式 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
}
}
总结
策略模式是应对算法变化的利器,它通过"封装变化"让系统更灵活。记住策略模式的核心思想:
找出代码中可能变化的部分,把它抽离出来,封装成可互换的策略。
在实际项目中,当您发现复杂的条件判断或经常需要添加新的算法变体时,就是使用策略模式的最佳时机。