目录
- 一、策略模式概述
- 二、策略模式的实现
-
- [2.1 策略接口](#2.1 策略接口)
- [2.2 具体策略类](#2.2 具体策略类)
- [2.3 上下文类](#2.3 上下文类)
- [2.4 客户端代码](#2.4 客户端代码)
- [2.5 UML类图](#2.5 UML类图)
- [2.6 UML时序图](#2.6 UML时序图)
- 三、优缺点
-
- [3.1 ✅优点](#3.1 ✅优点)
- [3.2 ❌ 缺点](#3.2 ❌ 缺点)
- 四、最佳实践场景
-
- [4.1 适合场景描述](#4.1 适合场景描述)
- [4.2 具体场景](#4.2 具体场景)
- 五、扩展
-
- [5.1 继承复用机制和复合策略](#5.1 继承复用机制和复合策略)
- [5.2 对象管理:优化策略类数量增加问题优化](#5.2 对象管理:优化策略类数量增加问题优化)
-
- [5.2.1 策略对象数量优化:将无状态策略设计为共享对象(使用 Flyweight 模式)](#5.2.1 策略对象数量优化:将无状态策略设计为共享对象(使用 Flyweight 模式))
- [5.2.2 上下文负责维护策略执行所需状态](#5.2.2 上下文负责维护策略执行所需状态)
- [5.2.3 轻量级策略推荐单例实现,重型策略考虑对象池](#5.2.3 轻量级策略推荐单例实现,重型策略考虑对象池)
- [5.3 策略与上下文的通信成本优化](#5.3 策略与上下文的通信成本优化)
-
- [5.3.1 参数封装](#5.3.1 参数封装)
- [5.3.2 适配器模式](#5.3.2 适配器模式)
- [5.4 客户端与策略解耦优化](#5.4 客户端与策略解耦优化)
- 六、使用建议
参考:
设计模式:可复用面向对象软件的基础(典藏版) - 5.9 Strategy(策略)------对象行为型模式 - 埃里克·伽玛 - 微信读书
项目地址:(https://github.com/Nita121388/NitasDemo/tree/main/10DesignPatterns/DesignPatterns/StrategyPattern)[策略模式]
相关标签:行为型设计模式、算法封装、切换、享元模式、工厂模式、
一、策略模式概述
策略模式(Strategy Pattern)是一种行为型设计模式,用于定义一系列算法,将每个算法封装起来,并使它们可以互换使用。策略模式让算法的变化独立于使用算法的客户。
核心思想:通过将算法封装为独立对象,实现运行时的算法选择。
-
动机
以开发一个文本编辑器为例,其中需要实现自动换行功能。这个功能可能有多种实现方式:简单换行(按字符数强制分割)、连字符优化换行等
现实开发中的痛点
- 扩展性差:混杂各种换行逻辑,核心功能被算法细节淹没。当新增加新功能时,需要冒着风险修改经过测试的编辑器核心类
- 灵活性不足:当不同文档类型需要不同换行策略时,需要复杂的条件判断
- 复用性低 :当需要为移动端定制换行策略时,不得不创建大量重复代码
策略模式如何解决问题?
通过将换行算法抽象为独立模块:
- 解耦核心逻辑:文本编辑器只需关注文本处理,不关心具体换行实现
- 动态切换策略:根据文档类型、设备类型或用户设置,在运行时自由切换算法
- 安全扩展:新增算法只需添加新类,无需修改现有代码(符合开闭原则)
- 清晰复用:其他需要换行功能的模块可以直接调用策略集合
-
核心概念/角色
- 📜策略接口(Strategy):定义了所有支持算法的公共接口,上下文(Context)使用这个接口来调用具体的策略类。
- ✨具体策略类(Concrete Strategy):实现了策略接口,提供具体的算法实现。
- 🔄上下文(Context):持有一个策略接口的引用,用于调用策略类的方法。上下文可以动态地改变其引用的策略对象。
-
策略模式核心价值
- 将算法封装为独立对象
- 实现运行时的灵活切换
- 提高系统的扩展性和维护性
关键洞察:策略模式不是简单地封装不同实现,而是通过建立清晰的算法供应链,让业务逻辑与具体算法实现形成「松耦合,高内聚」的协作关系。
二、策略模式的实现
2.1 策略接口
策略接口定义了所有策略类的公共方法,客户端通过这个接口与具体的策略类交互。
c#
public interface IStrategy
{
void Execute(int a, int b);
}
2.2 具体策略类
每个具体策略类都实现了策略接口,并提供了具体的算法实现。
c#
public class AddStrategy : IStrategy
{
public void Execute(int a, int b)
{
Console.WriteLine($"Add: {a} + {b} = {a + b}");
}
}
public class SubtractStrategy : IStrategy
{
public void Execute(int a, int b)
{
Console.WriteLine($"Subtract: {a} - {b} = {a - b}");
}
}
public class MultiplyStrategy : IStrategy
{
public void Execute(int a, int b)
{
Console.WriteLine($"Multiply: {a} * {b} = {a * b}");
}
}
2.3 上下文类
上下文类持有一个策略接口的引用,并通过这个引用调用具体的策略方法。上下文可以根据需要动态地更换策略。
c#
public class Context
{
private IStrategy _strategy;
public Context(IStrategy strategy)
{
_strategy = strategy;
}
public void SetStrategy(IStrategy strategy)
{
_strategy = strategy;
}
public void ExecuteStrategy(int a, int b)
{
_strategy.Execute(a, b);
}
}
2.4 客户端代码
客户端通过上下文类调用具体的策略方法,并可以在运行时动态更换策略。
c#
class Program
{
static void Main(string[] args)
{
// 创建具体策略
IStrategy addStrategy = new AddStrategy();
IStrategy subtractStrategy = new SubtractStrategy();
IStrategy multiplyStrategy = new MultiplyStrategy();
// 设置初始策略
Context context = new Context(addStrategy);
context.ExecuteStrategy(10, 5);
// 更改策略
context.SetStrategy(subtractStrategy);
context.ExecuteStrategy(10, 5);
// 更改策略
context.SetStrategy(multiplyStrategy);
context.ExecuteStrategy(10, 5);
}
}
2.5 UML类图
implements implements implements _strategy <<interface>> IStrategy +Execute(int a, int b) : void AddStrategy +Execute(int a, int b) : void SubtractStrategy +Execute(int a, int b) : void MultiplyStrategy +Execute(int a, int b) : void Context -_strategy: IStrategy +Context(IStrategy strategy) +SetStrategy(IStrategy strategy) : void +ExecuteStrategy(int a, int b) : void
2.6 UML时序图
Client Context AddStrategy SubtractStrategy MultiplyStrategy 创建 new AddStrategy() 创建 new SubtractStrategy() 创建 new MultiplyStrategy() 创建 new Context(addStrategy) ExecuteStrategy(10,5) Execute(10,5) 输出结果 完成 SetStrategy(subtractStrategy) 策略已切换 ExecuteStrategy(10,5) Execute(10,5) 输出结果 完成 SetStrategy(multiplyStrategy) 策略已切换 ExecuteStrategy(10,5) Execute(10,5) 输出结果 完成 Client Context AddStrategy SubtractStrategy MultiplyStrategy
三、优缺点
3.1 ✅优点
- 算法的独立性:每个算法封装在独立的类中,便于维护和单元测试。
- 消除条件分支 :替代了传统方式中繁琐的条件判断(如
if-else
或switch-case
结构),每个策略类封装独立行为逻辑,使代码更简洁、易读。 - 符合开闭原则:新增算法时只需添加新的策略类,无需修改现有的代码结构,系统对扩展开放、对修改关闭。
- 动态切换能力:运行时可灵活切换不同的策略实现,无需重新编译,系统更灵活。
- 代码清晰与可维护性:通过上下文(Context)与策略解耦,消除算法硬编码,使业务逻辑更清晰,减少维护成本。
3.2 ❌ 缺点
-
类的数量增加 : 每种策略需要一个独立的类,可能导致类的数量过多。
-
客户端需要了解所有策略 :客户端需要知道不同策略的具体实现,以便选择合适的策略。
-
可能导致过度设计 :对于简单的场景,使用策略模式可能显得过于复杂。
-
维护策略的上下文 :上下文需要维护与策略相关的状态,可能增加复杂性。
在策略模式中,上下文需要知道当前使用的是哪个策略,并且在一些情况下,上下文还需要保存一些与策略相关的状态。
举个例子:
一个订单处理系统,有不同的折扣策略,比如会员折扣、节日折扣等。这些策略可能会根据订单金额来计算折扣。
- 上下文的角色 :
- 上下文类需要知道当前的折扣策略是什么(比如是会员折扣还是节日折扣)。
- 它还需要保存订单金额这个状态,以便在计算折扣时使用。
- 复杂性问题 :
- 上下文需要管理这些状态信息,确保在切换策略时状态是正确的。
- 如果策略比较多,或者策略之间的状态依赖关系复杂,上下文的代码就会变得复杂,增加了理解和维护的难度。
- 上下文的角色 :
四、最佳实践场景
4.1 适合场景描述
- 需要多种算法变体时 🧮
- 需要运行时切换算法 🔄
- 需要隔离算法逻辑与业务逻辑📦
- 消除多个条件判断语句 🚮
4.2 具体场景
- 算法变体密集场景 :
- 如加密算法(AES、DES)需根据不同安全级别灵活切换。
- 支付网关:银联、支付宝、微信等支付策略。
- 业务规则波动频繁 :
- 促销活动规则(满减、折扣、赠品等)
- 物流配送策略(标准、特快、无人机)
- 复杂算法隔离需求 :
- 机器学习模型(决策树、神经网络、线性回归)
- 数据处理流程(批处理、流处理、实时处理)
- 多种验证方式 :
- 不同用户身份验证(人脸识别、指纹识别、短信验证码等)
五、扩展
5.1 继承复用机制和复合策略
- 继承复用机制:可以通过共同的接口或抽象类提取公共逻辑,减少重复代码,支持策略族的复用,促进算法演进。
- 复合策略扩展:支持组合多个策略应对复杂场景,如组合风险评估和税务优化策略,增强系统可扩展性。
以下是一个结合继承复用 和复合策略扩展的C#策略模式示例,以投资决策系统场景为例:
-
策略接口(抽象策略)
c#// 策略接口(抽象策略) public interface IInvestmentStrategy { void Execute(Portfolio portfolio); }
-
抽象策略基类(继承复用)
- 封装公共验证逻辑
ValidatePortfolio
- 强制子类实现核心算法
Execute
- 支持统一添加日志、监控等横切关注点
- 典型应用:所有具体策略继承公共验证和日志逻辑
c#// 抽象策略基类(继承复用) public abstract class CommonStrategy : IInvestmentStrategy { // 公共验证方法 protected void ValidatePortfolio(Portfolio portfolio) { if (portfolio.TotalValue <= 0) throw new ArgumentException("Invalid portfolio value"); } // 抽象方法要求子类必须实现 public abstract void Execute(Portfolio portfolio); }
- 封装公共验证逻辑
-
具体策略
-
具体策略1:风险评估策略(继承复用公共逻辑)
c#// 具体策略1:风险评估策略(继承复用公共逻辑) public class RiskAssessmentStrategy : CommonStrategy { public override void Execute(Portfolio portfolio) { ValidatePortfolio(portfolio); // 复用基类验证 // 具体风险计算逻辑 portfolio.RiskLevel = CalculateRisk(portfolio.Assets); Console.WriteLine($"风险评估完成,当前风险等级:{portfolio.RiskLevel}"); } private RiskLevel CalculateRisk(IEnumerable<Asset> assets) { // 实际风险计算逻辑 return RiskLevel.Moderate; } }
-
具体策略2:税务优化策略(继承复用公共逻辑)
c#// 具体策略2:税务优化策略(继承复用公共逻辑) public class TaxOptimizationStrategy : CommonStrategy { public override void Execute(Portfolio portfolio) { ValidatePortfolio(portfolio); // 复用基类验证 // 具体税务优化逻辑 portfolio.TaxBurden = OptimizeTax(portfolio.Investments); Console.WriteLine($"税务优化完成,税务负担减少:{portfolio.TaxBurden}%"); } private decimal OptimizeTax(IEnumerable<Investment> investments) { // 实际税务优化逻辑 return 15.5m; } }
-
具体策略3:流动性分析策略(继承复用公共逻辑)
c##region - 具体策略3:流动性分析策略(继承复用公共逻辑) public class LiquidityAnalysisStrategy : CommonStrategy { public override void Execute(Portfolio portfolio) { ValidatePortfolio(portfolio); // 复用基类验证 // 具体流动性分析逻辑 portfolio.LiquidityScore = CalculateLiquidity(portfolio.Assets); Console.WriteLine($"流动性分析完成,流动性得分:{portfolio.LiquidityScore}"); } private decimal CalculateLiquidity(IEnumerable<Asset> assets) { // 示例流动性计算逻辑:假设流动性得分基于资产价值的加权平均 decimal totalValue = assets.Sum(a => a.Value); decimal liquidityScore = assets.Sum(a => a.Value * a.LiquidityFactor) / totalValue; return liquidityScore; } } #endregion
-
-
复合策略(策略扩展)
- 动态组合多个策略(如风险+税务+流动性分析)
- 支持策略执行顺序控制(通过添加顺序)
- 允许运行时动态调整策略组合
- 典型应用:构建投资策略流水线
c#// 复合策略(策略扩展) public class CompositeStrategy : IInvestmentStrategy { private readonly List _strategies = new(); public void AddStrategy(IInvestmentStrategy strategy) { _strategies.Add(strategy); } public void Execute(Portfolio portfolio) { foreach (var strategy in _strategies) { strategy.Execute(portfolio); } } }
-
上下文类
c#// 上下文类 public class PortfolioManager { private IInvestmentStrategy _strategy; public void SetStrategy(IInvestmentStrategy strategy) { _strategy = strategy; } public void ExecuteStrategy(Portfolio portfolio) { _strategy?.Execute(portfolio); } }
-
Client Code 、使用示例
c#// 使用示例 var portfolio = new Portfolio(); var manager = new PortfolioManager(); // 使用单一策略 manager.SetStrategy(new RiskAssessmentStrategy()); manager.ExecuteStrategy(portfolio); // 使用复合策略 var composite = new CompositeStrategy(); composite.AddStrategy(new RiskAssessmentStrategy()); composite.AddStrategy(new TaxOptimizationStrategy()); composite.AddStrategy(new LiquidityAnalysisStrategy()); // 新增策略 manager.SetStrategy(composite); manager.ExecuteStrategy(portfolio);
输出
c#风险评估完成,当前风险等级:Moderate 风险评估完成,当前风险等级:Moderate 税务优化完成,税务负担减少:15.5% 流动性分析完成,流动性得分:0.8666666666666666666666666667
-
关键设计说明
- 策略族的统一管理(通过接口和抽象类)
- 算法实现的隔离变化(具体策略独立演进)
- 复杂业务场景的灵活组合(复合策略)
- 公共能力的集中维护(验证、日志等)
- 符合开闭原则
- 新增策略只需实现
IInvestmentStrategy
- 策略演进不影响客户端代码
- 复合策略可以嵌套其他复合策略
- 支持策略优先级配置(通过修改复合策略实现)
- 新增策略只需实现
5.2 对象管理:优化策略类数量增加问题优化
5.2.1 策略对象数量优化:将无状态策略设计为共享对象(使用 Flyweight 模式)
场景:假设有一个图像处理系统,支持多种图像滤镜(如黑白滤镜、模糊滤镜、锐化滤镜等)。这些滤镜是无状态的,即它们的行为不依赖于任何内部状态。
优化方式:使用 Flyweight 模式将无状态策略设计为共享对象。
-
定义策略接口
c#// 定义策略接口 public interface IFilterStrategy { void ApplyFilter(string image); }
-
具体策略类:黑白滤镜
c#// 具体策略类:黑白滤镜 public class BlackAndWhiteFilter : IFilterStrategy { public void ApplyFilter(string image) { Console.WriteLine($"Applying Black and White filter to {image}"); } }
-
具体策略类:模糊滤镜
c#// 具体策略类:模糊滤镜 public class BlurFilter : IFilterStrategy { public void ApplyFilter(string image) { Console.WriteLine($"Applying Blur filter to {image}"); } }
-
策略工厂类(Flyweight 模式)
- Flyweight 模式的核心 :通过一个静态字典
_filters
来存储已经创建的策略对象。这样可以避免重复创建相同类型的策略对象。 - 单例模式的应用 :由于
_filters
是静态的,FilterFactory
类在整个程序中只会维护一份策略对象的映射关系。 - 策略对象的复用 :当客户端请求某个类型的滤镜时,
GetFilter
方法会先检查_filters
是否已经存在该类型的策略对象。如果不存在,则创建一个新的策略对象并存储在字典中;如果已经存在,则直接返回已有的对象。 - 优化效果:通过这种方式,系统中每种类型的策略对象只会被创建一次,大大减少了对象的实例化数量,节省了内存。
c#// 策略工厂类(Flyweight 模式) public class FilterFactory { private static readonly Dictionary<string, IFilterStrategy> _filters = new Dictionary<string, IFilterStrategy>(); public static IFilterStrategy GetFilter(string filterType) { if (!_filters.ContainsKey(filterType)) { switch (filterType) { case "BlackAndWhite": _filters[filterType] = new BlackAndWhiteFilter(); break; case "Blur": _filters[filterType] = new BlurFilter(); break; default: throw new ArgumentException("Invalid filter type"); } } return _filters[filterType]; } }
- Flyweight 模式的核心 :通过一个静态字典
-
上下文类
c#// 上下文类 public class ImageProcessor { private IFilterStrategy _filterStrategy; public void SetFilterStrategy(IFilterStrategy filterStrategy) { _filterStrategy = filterStrategy; } public void ProcessImage(string image) { _filterStrategy.ApplyFilter(image); } }
-
Client Code
c#using System; // 客户端代码 public class Program { public static void Main() { var processor = new ImageProcessor(); // 使用共享的黑白滤镜策略对象 processor.SetFilterStrategy(FilterFactory.GetFilter("BlackAndWhite")); processor.ProcessImage("image1.jpg"); // 使用共享的模糊滤镜策略对象 processor.SetFilterStrategy(FilterFactory.GetFilter("Blur")); processor.ProcessImage("image2.jpg"); } }
结果
c#Applying Black and White filter to image1.jpg Applying Blur filter to image2.jpg
5.2.2 上下文负责维护策略执行所需状态
场景:假设我们有一个订单处理系统,支持不同的折扣策略(如会员折扣、节日折扣等)。这些策略可能需要访问上下文中的状态(如订单金额)。
优化方式:上下文负责维护策略执行所需的状态。
-
定义策略接口
c#// 定义策略接口 public interface IDiscountStrategy { decimal ApplyDiscount(decimal orderAmount); }
-
具体策略类:会员折扣 和 具体策略类:节日折扣
c#// 具体策略类:会员折扣 public class MemberDiscount : IDiscountStrategy { public decimal ApplyDiscount(decimal orderAmount) { return orderAmount * 0.9m; // 10% 折扣 } } // 具体策略类:节日折扣 public class HolidayDiscount : IDiscountStrategy { public decimal ApplyDiscount(decimal orderAmount) { return orderAmount * 0.8m; // 20% 折扣 } }
-
上下文类
c#public class OrderProcessor { private IDiscountStrategy _discountStrategy; // 当前使用的折扣策略 private decimal _orderAmount; // 订单金额,策略执行所需的状态 public OrderProcessor(decimal orderAmount) { _orderAmount = orderAmount; // 初始化订单金额 } public void SetDiscountStrategy(IDiscountStrategy discountStrategy) { _discountStrategy = discountStrategy; // 设置当前的折扣策略 } public decimal ProcessOrder() { return _discountStrategy.ApplyDiscount(_orderAmount); // 调用策略对象的 ApplyDiscount 方法 } }
OrderProcessor
类是上下文类,它负责维护订单金额_orderAmount
,并提供一个方法来设置折扣策略_discountStrategy
。它的主要职责是:- 维护状态 :保存订单金额
_orderAmount
,这是策略执行时需要使用的状态。 - 策略切换 :通过
SetDiscountStrategy
方法动态设置不同的折扣策略。 - 执行策略 :在
ProcessOrder
方法中调用当前设置的折扣策略的ApplyDiscount
方法,并将订单金额传递给策略对象。
- 维护状态 :保存订单金额
-
Client
c#var order = new OrderProcessor(100m); // 使用会员折扣策略 order.SetDiscountStrategy(new MemberDiscount()); Console.WriteLine($"Member Discount: {order.ProcessOrder()}"); // 使用节日折扣策略 order.SetDiscountStrategy(new HolidayDiscount()); Console.WriteLine($"Holiday Discount: {order.ProcessOrder()}");
解释 :OrderProcessor
上下文类负责维护订单金额,并将其传递给策略对象。这样,策略对象不需要自己维护状态,减少了类的复杂性。
5.2.3 轻量级策略推荐单例实现,重型策略考虑对象池
场景:假设我们有一个日志系统,支持不同的日志输出策略(如控制台输出、文件输出等)。控制台输出策略是轻量级的,而文件输出策略是资源密集型的。
优化方式:轻量级策略使用单例模式,重型策略使用对象池。
-
策略接口定义
c#public interface ILogStrategy { void Log(string message); }
-
具体策略类:控制台输出策略(单例模式)
说明:
- 实现了
ILogStrategy
接口,用于将日志输出到控制台。 - 使用了单例模式,确保整个程序中只有一个
ConsoleLogStrategy
实例。 Log
方法将日志信息以控制台输出的形式展示。
c#public class ConsoleLogStrategy : ILogStrategy { private static readonly ConsoleLogStrategy _instance = new ConsoleLogStrategy(); private ConsoleLogStrategy() { } public static ConsoleLogStrategy Instance => _instance; public void Log(string message) { Console.WriteLine($"Console Log: {message}"); } }
- 实现了
-
具体策略类:文件输出策略
c#public class FileLogStrategy : ILogStrategy { public void Log(string message) { Console.WriteLine($"File Log: {message}"); } }
-
对象池管理文件输出策略
说明:
- 使用对象池模式管理
FileLogStrategy
实例。 - 静态构造函数初始化了对象池,预先创建了5个
FileLogStrategy
实例。 Acquire
方法用于从池中获取一个实例,如果池为空,则创建新的实例。Release
方法将使用完毕的实例放回对象池,以便复用。
c#public class FileLogStrategyPool { private static readonly Queue _pool = new Queue(); static FileLogStrategyPool() { for (int i = 0; i < 5; i++) { _pool.Enqueue(new FileLogStrategy()); } } public static FileLogStrategy Acquire() { if (_pool.Count > 0) { return _pool.Dequeue(); } return new FileLogStrategy(); } public static void Release(FileLogStrategy strategy) { _pool.Enqueue(strategy); } }
- 使用对象池模式管理
-
上下文类:日志记录器
说明:
Logger
类是策略模式的上下文类,用于封装日志记录的逻辑。- 客户端可以通过
SetLogStrategy
方法动态设置日志策略。 Log
方法调用当前策略的Log
方法,实现日志记录。
c#public class Logger { private ILogStrategy _logStrategy; public void SetLogStrategy(ILogStrategy logStrategy) { _logStrategy = logStrategy; } public void Log(string message) { _logStrategy.Log(message); } }
-
客户端代码
说明:
- 方式1:使用单例的
ConsoleLogStrategy
将日志输出到控制台。 - 方式2:从对象池中获取一个
FileLogStrategy
实例,将日志输出到文件,并在使用完毕后将其释放回对象池。
c#var logger = new Logger(); // 使用单例的控制台输出策略 logger.SetLogStrategy(ConsoleLogStrategy.Instance); logger.Log("This is a log message."); // 使用对象池的文件输出策略 var fileLogStrategy = FileLogStrategyPool.Acquire(); logger.SetLogStrategy(fileLogStrategy); logger.Log("This is a file log message."); FileLogStrategyPool.Release(fileLogStrategy);
输出
c#Console Log: This is a log message. File Log: This is a file log message.
- 方式1:使用单例的
解释 :ConsoleLogStrategy
使用单例模式来减少内存消耗,而 FileLogStrategy
使用对象池来管理资源密集型对象的生命周期,避免频繁的创建与销毁。
5.3 策略与上下文的通信成本优化
当不同策略需要不同参数时,上下文需处理多种参数类型,导致接口复杂且类型不安全。
5.3.1 参数封装
将参数封装到统一对象中,减少接口复杂性。
-
参数封装类用于存储支付过程中需要的参数信息。
说明:
此部分将支付相关的参数封装到一个类中,方便在不同支付策略中传递和使用。
这种封装方式提高了代码的可维护性和可扩展性。
c#public class PaymentParameters { public string CardNumber { get; set; } // 信用卡卡号 public string SecurityCode { get; set; } // 信用卡安全码 public string PhoneNumber { get; set; } // 支付宝绑定的手机号 }
-
策略接口:
IPaymentStrategy
- 策略模式的核心是定义一个通用接口,让不同的策略类实现该接口。
- 这里定义了一个
ProcessPayment
方法,所有具体的支付策略类(如信用卡支付、支付宝支付)都必须实现该方法。
c#public interface IPaymentStrategy { void ProcessPayment(double amount, PaymentParameters parameters); // 定义支付方法 }
-
具体策略类
-
信用卡支付策略:
CreditCardStrategy
说明:
- 这是信用卡支付的具体实现。
- 在支付前,会检查信用卡号和安全码是否为空。如果为空,抛出异常。
- 如果信息完整,则输出支付信息。
c#public class CreditCardStrategy : IPaymentStrategy { public void ProcessPayment(double amount, PaymentParameters parameters) { if (string.IsNullOrEmpty(parameters.CardNumber) || string.IsNullOrEmpty(parameters.SecurityCode)) { throw new ArgumentException("信用卡信息缺失"); } Console.WriteLine($"信用卡支付:金额={amount}, 卡号={parameters.CardNumber}, 安全码={parameters.SecurityCode}"); } }
-
支付宝支付策略:
AlipayStrategy
说明:
- 这是支付宝支付的具体实现。
- 在支付前,会检查手机号是否为空。如果为空,抛出异常。
- 如果手机号有效,则输出支付信息。
c#public class AlipayStrategy : IPaymentStrategy { public void ProcessPayment(double amount, PaymentParameters parameters) { if (string.IsNullOrEmpty(parameters.PhoneNumber)) { throw new ArgumentException("手机号缺失"); } Console.WriteLine($"支付宝支付:金额={amount}, 手机号={parameters.PhoneNumber}"); } }
-
-
上下文类:
PaymentContext
上下文类用于动态切换支付策略,并调用对应的支付方法。
说明:
PaymentContext
类提供了一个接口来设置和执行支付策略。_strategy
属性用于存储当前的支付策略。SetStrategy
方法用于动态切换支付策略。ExecutePayment
方法调用当前策略的ProcessPayment
方法,完成支付操作。
c#public class PaymentContext { private IPaymentStrategy _strategy; // 当前使用的支付策略 public void SetStrategy(IPaymentStrategy strategy) { _strategy = strategy; // 设置当前支付策略 } public void ExecutePayment(double amount, PaymentParameters parameters) { _strategy?.ProcessPayment(amount, parameters); // 调用当前策略的支付方法 } }
-
Client Code
说明:
- 创建了
PaymentParameters
参数对象并设置了支付参数。 - 创建了
PaymentContext
对象,并通过SetStrategy
方法动态切换支付策略。 - 调用
ExecutePayment
方法执行支付操作。 - 通过切换策略,可以轻松地在不同支付方式之间切换,展示了策略模式的灵活性。
c#public class Program { public static void Main() { var parameters = new PaymentParameters { CardNumber = "1234-5678-9012-3456", // 信用卡卡号 SecurityCode = "123", // 信用卡安全码 PhoneNumber = "13812345678" // 支付宝绑定的手机号 }; var context = new PaymentContext(); // 使用信用卡支付 context.SetStrategy(new CreditCardStrategy()); context.ExecutePayment(100.0, parameters); // 切换为支付宝支付 context.SetStrategy(new AlipayStrategy()); context.ExecutePayment(200.0, parameters); } }
输出
c#信用卡支付:金额=100, 卡号=1234-5678-9012-3456, 安全码=123 支付宝支付:金额=200, 手机号=13812345678
- 创建了
5.3.2 适配器模式
通过适配器将不同参数转换为通用格式,简化交互。
-
通用参数接口(
IPaymentParameters
)-
说明:
定义了一个通用接口,用于获取支付参数。
GetParameter<T>
方法通过泛型和键值对的方式,允许不同类型的支付方式提供参数访问功能。
c#public interface IPaymentParameters { T GetParameter<T>(string key); }
-
-
参数适配器
-
信用卡参数适配器(
CreditCardAdapter
)- 功能说明 :
- 信用卡支付需要提供卡号和安全码。
- 通过
GetParameter<T>
方法,根据键值返回对应的参数。
c#public class CreditCardAdapter : IPaymentParameters { private readonly string _cardNumber; private readonly string _securityCode; public CreditCardAdapter(string cardNumber, string securityCode) { _cardNumber = cardNumber; _securityCode = securityCode; } public T GetParameter<T>(string key) { switch (key) { case "CardNumber": return (T)(object)_cardNumber; case "SecurityCode": return (T)(object)_securityCode; default: throw new KeyNotFoundException($"参数 {key} 不存在"); } } }
- 功能说明 :
-
支付宝参数适配器(
AlipayAdapter
)- 功能说明 :
- 支付宝支付需要提供手机号。
- 通过
GetParameter<T>
方法,根据键值返回对应的参数。
c#public class AlipayAdapter : IPaymentParameters { private readonly string _phoneNumber; public AlipayAdapter(string phoneNumber) { _phoneNumber = phoneNumber; } public T GetParameter<T>(string key) { if (key == "PhoneNumber") return (T)(object)_phoneNumber; throw new KeyNotFoundException($"参数 {key} 不存在"); } }
- 功能说明 :
-
-
策略接口(
IPaymentStrategy
)- 功能说明 :
- 定义了支付策略的通用接口。
ProcessPayment
方法用于处理支付逻辑,接受金额和支付参数。
c#public interface IPaymentStrategy { void ProcessPayment(double amount, IPaymentParameters parameters); }
- 功能说明 :
-
具体策略类
-
信用卡支付策略(
CreditCardStrategy
)- 功能说明 :
- 从参数适配器中获取卡号和安全码。
- 打印信用卡支付的详细信息。
c#public class CreditCardStrategy : IPaymentStrategy { public void ProcessPayment(double amount, IPaymentParameters parameters) { string cardNumber = parameters.GetParameter<string>("CardNumber"); string securityCode = parameters.GetParameter<string>("SecurityCode"); Console.WriteLine($"信用卡支付:金额={amount}, 卡号={cardNumber}, 安全码={securityCode}"); } }
- 功能说明 :
-
支付宝支付策略(
AlipayStrategy
)- 功能说明 :
- 从参数适配器中获取手机号。
- 打印支付宝支付的详细信息。
c#public class AlipayStrategy : IPaymentStrategy { public void ProcessPayment(double amount, IPaymentParameters parameters) { string phone = parameters.GetParameter<string>("PhoneNumber"); Console.WriteLine($"支付宝支付:金额={amount}, 手机号={phone}"); } }
- 功能说明 :
-
-
上下文类(
PaymentContext
)- 功能说明 :
- 管理具体的支付策略。
- 通过
SetStrategy
方法设置支付策略。 - 通过
ExecutePayment
方法执行支付操作。
c#public class PaymentContext { private IPaymentStrategy _strategy; public void SetStrategy(IPaymentStrategy strategy) { _strategy = strategy; } public void ExecutePayment(double amount, IPaymentParameters parameters) { _strategy?.ProcessPayment(amount, parameters); } }
- 功能说明 :
-
使用示例(
Program
类)- 功能说明 :
- 创建支付上下文。
- 设置不同的支付策略并执行支付操作。
- 演示了如何通过策略模式切换不同的支付方式。
c#public class Program { public static void Main() { var context = new PaymentContext(); // 信用卡支付 var creditCardParams = new CreditCardAdapter("1234-5678-9012-3456", "123"); context.SetStrategy(new CreditCardStrategy()); context.ExecutePayment(100.0, creditCardParams); // 支付宝支付 var alipayParams = new AlipayAdapter("13812345678"); context.SetStrategy(new AlipayStrategy()); context.ExecutePayment(200.0, alipayParams); } }
- 功能说明 :
-
类图
聚合 依赖 依赖 依赖 依赖 依赖 <<interface>> IPaymentParameters +GetParameter~T~(string key) CreditCardAdapter -_cardNumber: string -_securityCode: string +CreditCardAdapter(string, string) +GetParameter~T~(string key) AlipayAdapter -_phoneNumber: string +AlipayAdapter(string) +GetParameter~T~(string key) <<interface>> IPaymentStrategy +ProcessPayment(double, IPaymentParameters) : void CreditCardStrategy +ProcessPayment(double, IPaymentParameters) : void AlipayStrategy +ProcessPayment(double, IPaymentParameters) : void PaymentContext -_strategy: IPaymentStrategy +SetStrategy(IPaymentStrategy) : void +ExecutePayment(double, IPaymentParameters) : void Program +Main() : void
-
时序图
Program PaymentContext CreditCardStrategy CreditCardAdapter AlipayStrategy AlipayAdapter Console 信用卡支付流程 创建实例 创建实例("1234...", "123") SetStrategy(CreditCardStrategy) ExecutePayment(100.0, creditCardParams) ProcessPayment(100.0, creditCardParams) GetParameter<string>("CardNumber") "1234..." GetParameter<string>("SecurityCode") "123" 打印支付信息 支付宝支付流程 创建实例("138...") SetStrategy(AlipayStrategy) ExecutePayment(200.0, alipayParams) ProcessPayment(200.0, alipayParams) GetParameter<string>("PhoneNumber") "138..." 打印支付信息 Program PaymentContext CreditCardStrategy CreditCardAdapter AlipayStrategy AlipayAdapter Console
5.4 客户端与策略解耦优化
-
在传统的策略模式中,客户端需要了解所有策略类的具体实现细节,并负责选择合适的策略。然而,为了实现一个灵活的支付策略模式,我们可以通过解耦客户端代码与具体策略的方式,使新增支付方式时只需注册新的策略,而无需修改客户端代码。具体实现方式如下:
- 引入工厂模式:使用工厂模式来创建策略对象,客户端只需知道工厂接口,而无需了解具体的策略实现。
- 策略注册与动态选择:通过注册机制,允许策略在运行时动态选择,客户端只需提供策略标识或条件,而不需了解具体的策略实现。
- 使用配置文件:通过配置文件或注解来管理策略的选择,客户端可以通过配置而不是硬编码来选择策略。
-
策略接口
c#// 策略接口保持不变 public interface IPaymentStrategy { void ProcessPayment(decimal amount); }
-
策略工厂与注册中心
策略工厂负责管理和注册具体的支付策略,提供策略的获取方式。
说明:
_strategies
是一个静态字典,用于存储策略的键值对。RegisterStrategy
方法用于将策略注册到工厂中,确保每个策略键是唯一的。GetStrategy
方法通过键获取对应的策略实例,如果键不存在则抛出异常。
c#// 策略工厂+注册中心 public class PaymentStrategyFactory { private static readonly Dictionary _strategies = new(); // 注册策略 public static void RegisterStrategy(string key, IPaymentStrategy strategy) { if (!_strategies.ContainsKey(key)) { _strategies.Add(key, strategy); } } // 获取策略 public static IPaymentStrategy GetStrategy(string key) { if (_strategies.TryGetValue(key, out var strategy)) { return strategy; } throw new KeyNotFoundException($"未找到支付策略:{key}"); } }
-
配置中心
配置中心模拟了外部配置文件(如
appsettings.json
),用于定义支付策略的映射关系。说明:
PaymentStrategies
是一个字典,定义了支付类型与具体策略的映射关系。- 例如,
CreditCard
映射到CreditCardPayment
,PayPal
映射到PayPalPayment
。 - 还定义了一个默认策略(
Default
),用于处理未明确指定的支付类型。
c#// 配置中心(模拟appsettings.json) public static class AppConfig { public static readonly Dictionary<string, string> PaymentStrategies = new() { { "CreditCard", "CreditCardPayment" }, { "PayPal", "PayPalPayment" }, { "Default", "CreditCard" } }; }
-
初始化注册
在程序启动时,需要将具体的支付策略注册到工厂中。
说明:
- 这里注册了两种支付策略:
CreditCardPayment
和PayPalPayment
。 - 这些策略需要实现
IPaymentStrategy
接口,并在程序启动时完成注册。
c#// 初始化注册(通常在程序启动时) PaymentStrategyFactory.RegisterStrategy("CreditCardPayment", new CreditCardPayment()); PaymentStrategyFactory.RegisterStrategy("PayPalPayment", new PayPalPayment());
- 这里注册了两种支付策略:
-
客户端代码 Client Code
客户端代码实现了支付逻辑的调用,通过配置中心和策略工厂获取具体的支付策略。
说明:
- 客户端通过传入的
paymentType
,从配置中心获取对应的策略键。 - 如果未找到匹配的策略键,则使用默认策略。
- 使用策略工厂获取具体的策略实例,并调用其
ProcessPayment
方法完成支付。
c#// 优化后的客户端 class OptimizedClient { public void Checkout(string paymentType) { // 通过配置映射获取实际策略key var strategyKey = AppConfig.PaymentStrategies.TryGetValue(paymentType, out var key) ? key : AppConfig.PaymentStrategies["Default"]; var strategy = PaymentStrategyFactory.GetStrategy(strategyKey); strategy.ProcessPayment(100); } }
输出
textProcessing Credit Card payment for amount: 100 Processing PayPal payment for amount: 100
- 客户端通过传入的
-
类图
实现 实现 查询策略映射 获取策略实例 聚合存储策略实例 OptimizedClient +Checkout(string paymentType) <<static>> AppConfig +PaymentStrategies: Dictionary<string, string> <<static>> PaymentStrategyFactory -_strategies: Dictionary<string, IPaymentStrategy> +RegisterStrategy(string key, IPaymentStrategy strategy) +GetStrategy(string key) : IPaymentStrategy <<interface>> IPaymentStrategy +ProcessPayment(decimal amount) CreditCardPayment +ProcessPayment(decimal amount) PayPalPayment +ProcessPayment(decimal amount)
-
时序图
Client OptimizedClient AppConfig PaymentStrategyFactory CreditCardPayment Checkout("CreditCard") PaymentStrategies.TryGetValue("CreditCard") "CreditCardPayment" GetStrategy("CreditCardPayment") CreditCardPayment实例 ProcessPayment(100) 执行支付完成 Client OptimizedClient AppConfig PaymentStrategyFactory CreditCardPayment
六、使用建议
⚠️ 注意事项:
- 客户端需理解策略间的功能差异
- 避免过度设计简单算法场景
- 策略接口设计应保持适度抽象
- 权衡模式引入的架构复杂度
- 当存在多个相似类仅在行为不同时优先考虑
- 配合工厂模式使用可以更好地管理策略对象
- 注意控制策略类的数量膨胀