概念
策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,并将每个算法封装成单独的对象,使它们可以相互替换。策略模式允许算法的变化独立于使用算法的客户端
策略模式通常涉及三个核心角色:上下文(Context)、策略接口(Strategy Interface)和具体策略类(Concrete Strategies)
上下文(Context)
负责维护对策略对象的引用,并在需要时调用策略对象的算法
上下文通常会将请求委派给策略对象来执行特定的算法。
策略接口(Strategy Interface)
定义了所有具体策略类必须实现的算法接口
这个接口通常只有一个方法,用于执行具体的算法。
具体策略类(Concrete Strategies)
实现了策略接口,包含了具体的算法实现
每个具体策略类代表了一个具体的算法,可以根据需求增加或修改
策略模式的核心思想是将算法封装成独立的对象,并使这些对象可以相互替换,从而使得算法的变化不会影响到使用算法的客户端
这种模式的优点在于,提高了代码的灵活性和可维护性,使得算法可以在不修改客户端代码的情况下进行替换或者扩展
策略模式适用于以下情况:
- 当一个系统需要支持多种算法,并且这些算法可以相互替换时,可以使用策略模式
- 当一个类的行为取决于一些动态变化的条件时,可以考虑使用策略模式
举个简单的例子,考虑一个电商系统中的支付功能
系统可以支持多种支付方式,如支付宝、微信支付、信用卡支付等
策略模式可以将每种支付方式封装成一个具体的策略类,然后根据用户选择的支付方式来动态地选择并使用相应的支付策略,从而实现支付功能的灵活性和可扩展性
实现条件
存在一组相关的算法
策略模式适用于存在一组相关的算法,并且客户端需要在运行时选择其中一个算法来使用的情况
算法之间可以相互替换
策略模式的核心思想是将算法封装成策略对象,并且允许客户端在不修改客户端代码的情况下替换算法
需要避免使用条件语句来选择算法
如果存在多个条件语句来选择不同的算法,可能会导致代码的可读性和可维护性下降
策略模式可以将条件语句替换为对象之间的关系,从而提高代码的可读性和可维护性
需要将算法的实现和使用分离
策略模式将算法的实现和使用分离,使得客户端可以独立于具体算法进行变化,从而提高系统的灵活性和可扩展性
需要将变化的部分封装成独立的对象
策略模式将算法封装成策略对象,并将策略对象作为客户端和上下文对象的一部分,从而将变化的部分封装成独立的对象,方便扩展和修改
优点
灵活性高
策略模式允许客户端在运行时动态地选择算法,从而提高系统的灵活性和可扩展性
可维护性好
策略模式将算法封装成独立的策略对象,使得算法的实现和使用分离,从而提高了代码的可维护性
避免使用条件语句
策略模式避免了使用过多的条件语句来选择不同的算法,从而提高了代码的可读性和可维护性
增加代码复用性
策略模式将算法封装成独立的策略对象,可以在不同的上下文中重复使用相同的算法,从而提高了代码的复用性
符合开闭原则
策略模式通过定义一组算法族,并通过策略对象进行封装和使用,可以方便地添加新的算法或修改现有算法,从而符合开闭原则
缺点
增加对象数量
策略模式将每个算法都封装成独立的策略对象,可能会增加对象数量,从而增加了系统的复杂度
客户端需要了解所有策略
客户端需要了解所有可用的策略,并在运行时选择合适的策略,可能会增加客户端的复杂度
可能增加系统的运行开销
策略模式需要在运行时动态地选择算法,可能会增加系统的运行开销
实现方式
支付类实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
|
class PaymentStrategyFactory {
constructor() { this.strategies = new Map() }
register(name, strategy) { if (!(strategy instanceof PaymentStrategy)) { throw new Error("注册的策略必须实现PaymentStrategy接口") } this.strategies.set(name, strategy) }
unregister(name) { if (this.strategies.has(name)) { this.strategies.delete(name) } else { console.warn(`尝试注销不存在的支付策略: ${name}`) } }
getStrategy(name) { const strategy = this.strategies.get(name) if (!strategy) { throw new Error(`未找到名为 ${name} 的支付策略`) } return strategy } }
|
基础支付类实现
1 2 3 4 5 6 7
| class BasicPaymentStrategy { pay(amount) { throw new Error("pay() 方法必须在子类中实现") } }
export default BasicPaymentStrategy
|
收银台类实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
class Checkout {
constructor(paymentFactory) { this.paymentFactory = paymentFactory }
processPayment(amount, strategyName) { const strategy = this.paymentFactory.getStrategy(strategyName) strategy.pay(amount) } }
|
具体支付类实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
import BasicPaymentStrategy from '../Basic/BasicPaymentStrategy'
class AliPayStrategy extends BasicPaymentStrategy {
constructor() { super() this._partnerId = null this._sellerId = null }
setCredentials(partnerId, sellerId) { if (!partnerId || !sellerId) { throw new Error('partnerId 和 sellerId 都是必填项') } this._partnerId = partnerId this._sellerId = sellerId return this }
pay(amount) { if (!this._partnerId || !this._sellerId) { throw new Error('请先调用 setCredentials 设置 PartnerId 和 SellerId') } console.log(`使用支付宝支付,PartnerId: ${this._partnerId}, SellerId: ${this._sellerId}, 金额: ${amount}`) } }
export default new AliPayStrategy
|
怎么使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
import { CheckStand, PaymentStrategy } from '../StrategyPattern' import WeChatPayStrategy from '../SubClass/Expansion/WeChatPayStrategy' import AliPayStrategy from '../SubClass/Expansion/AliPayStrategy' import creditCardStrategy from '../SubClass/Expansion/CreditCardStrategy'
PaymentStrategy.register("wechat", WeChatPayStrategy.setCredentials("wxAppId123", "apiKey456")) PaymentStrategy.register("alipay", AliPayStrategy.setCredentials("aliPartnerId789", "sellerId012")) PaymentStrategy.register("creditCard", creditCardStrategy.setCardDetails('1234567890123456', '123', '12/23'))
CheckStand.processPayment(100, "wechat") CheckStand.processPayment(200, "alipay") CheckStand.processPayment(200, "creditCard")
class NewPaymentStrategy extends PaymentStrategy { } PaymentStrategy.register("newMethod", NewPaymentStrategy.setCredentials())
CheckStand.processPayment(50, "newMethod")
PaymentStrategy.unregister("wechat")
|
场景
表单验证
当需要根据不同的条件执行不同的验证逻辑时,可以使用策略模式
例如,根据用户输入的不同数据类型(文本、数字、日期等),选择不同的验证算法
排序算法
如果一个应用程序需要支持多种排序算法(如冒泡排序、快速排序、归并排序等),可以使用策略模式
根据数据量大小、数据结构等因素,选择合适的排序策略
价格计算
在线购物网站可能需要根据不同的促销活动或会员级别来计算产品的价格
使用策略模式可以使得价格计算算法独立于产品和促销活动的变化
缓存策略
在应用程序中实现缓存时,可能会有多种缓存策略可供选择,例如基于时间过期的策略、基于访问频率的策略等
策略模式可以用于管理这些不同的缓存策略
日志记录
根据日志级别(如调试、信息、警告、错误等),选择不同的日志记录策略
例如,在生产环境中可能只记录错误日志,而在开发环境中记录所有日志
源代码