当前位置: 首页 > article >正文

设计模式|策略模式 Strategy Pattern 详解

目录

  • 一、策略模式概述
  • 二、策略模式的实现
    • 2.1 策略接口
    • 2.2 具体策略类
    • 2.3 上下文类
    • 2.4 客户端代码
    • 2.5 UML类图
    • 2.6 UML时序图
  • 三、优缺点
    • 3.1 ✅优点
    • 3.2 ❌ 缺点
  • 四、最佳实践场景
    • 4.1 适合场景描述
    • 4.2 具体场景
  • 五、扩展
    • 5.1 继承复用机制和复合策略
    • 5.2 对象管理:优化策略类数量增加问题优化
      • 5.2.1 策略对象数量优化:将无状态策略设计为共享对象(使用 Flyweight 模式)
      • 5.2.2 上下文负责维护策略执行所需状态
      • 5.2.3 轻量级策略推荐单例实现,重型策略考虑对象池
    • 5.3 策略与上下文的通信成本优化
      • 5.3.1 参数封装
      • 5.3.2 适配器模式
    • 5.4 客户端与策略解耦优化
  • 六、使用建议

参考:
设计模式:可复用面向对象软件的基础(典藏版) - 5.9 Strategy(策略)——对象行为型模式 - 埃里克·伽玛 - 微信读书

项目地址:(https://github.com/Nita121388/NitasDemo/tree/main/10DesignPatterns/DesignPatterns/StrategyPattern)[策略模式]

相关标签:行为型设计模式、算法封装、切换、享元模式、工厂模式、

一、策略模式概述

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

核心思想:通过将算法封装为独立对象,实现运行时的算法选择。

  • 动机

    以开发一个文本编辑器为例,其中需要实现自动换行功能。这个功能可能有多种实现方式:简单换行(按字符数强制分割)、连字符优化换行等

    现实开发中的痛点

    • 扩展性差:混杂各种换行逻辑,核心功能被算法细节淹没。当新增加新功能时,需要冒着风险修改经过测试的编辑器核心类
    • 灵活性不足:当不同文档类型需要不同换行策略时,需要复杂的条件判断
    • 复用性低:当需要为移动端定制换行策略时,不得不创建大量重复代码
      策略模式如何解决问题?
      通过将换行算法抽象为独立模块:
    1. 解耦核心逻辑:文本编辑器只需关注文本处理,不关心具体换行实现
    2. 动态切换策略:根据文档类型、设备类型或用户设置,在运行时自由切换算法
    3. 安全扩展:新增算法只需添加新类,无需修改现有代码(符合开闭原则)
    4. 清晰复用:其他需要换行功能的模块可以直接调用策略集合
  • 核心概念/角色

    • 📜策略接口(Strategy):定义了所有支持算法的公共接口,上下文(Context)使用这个接口来调用具体的策略类。
    • 具体策略类(Concrete Strategy):实现了策略接口,提供具体的算法实现。
    • 🔄上下文(Context):持有一个策略接口的引用,用于调用策略类的方法。上下文可以动态地改变其引用的策略对象。
  • 策略模式核心价值

    • 将算法封装为独立对象
    • 实现运行时的灵活切换
    • 提高系统的扩展性和维护性

    关键洞察:策略模式不是简单地封装不同实现,而是通过建立清晰的算法供应链,让业务逻辑与具体算法实现形成「松耦合,高内聚」的协作关系。

二、策略模式的实现

2.1 策略接口

策略接口定义了所有策略类的公共方法,客户端通过这个接口与具体的策略类交互。

public interface IStrategy
{void Execute(int a, int b);
}

2.2 具体策略类

每个具体策略类都实现了策略接口,并提供了具体的算法实现。

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 上下文类

上下文类持有一个策略接口的引用,并通过这个引用调用具体的策略方法。上下文可以根据需要动态地更换策略。

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 客户端代码

客户端通过上下文类调用具体的策略方法,并可以在运行时动态更换策略。

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 ✅优点

  1. 算法的独立性:每个算法封装在独立的类中,便于维护和单元测试。
  2. 消除条件分支:替代了传统方式中繁琐的条件判断(如if-elseswitch-case结构),每个策略类封装独立行为逻辑,使代码更简洁、易读。
  3. 符合开闭原则:新增算法时只需添加新的策略类,无需修改现有的代码结构,系统对扩展开放、对修改关闭。
  4. 动态切换能力:运行时可灵活切换不同的策略实现,无需重新编译,系统更灵活。
  5. 代码清晰与可维护性:通过上下文(Context)与策略解耦,消除算法硬编码,使业务逻辑更清晰,减少维护成本。

3.2 ❌ 缺点

  1. 类的数量增加 : 每种策略需要一个独立的类,可能导致类的数量过多。

  2. 客户端需要了解所有策略 :客户端需要知道不同策略的具体实现,以便选择合适的策略。

  3. 可能导致过度设计 :对于简单的场景,使用策略模式可能显得过于复杂。

  4. 维护策略的上下文 :上下文需要维护与策略相关的状态,可能增加复杂性。

    在策略模式中,上下文需要知道当前使用的是哪个策略,并且在一些情况下,上下文还需要保存一些与策略相关的状态。

    举个例子:

    一个订单处理系统,有不同的折扣策略,比如会员折扣、节日折扣等。这些策略可能会根据订单金额来计算折扣。

    1. 上下文的角色
      • 上下文类需要知道当前的折扣策略是什么(比如是会员折扣还是节日折扣)。
      • 它还需要保存订单金额这个状态,以便在计算折扣时使用。
    2. 复杂性问题
      • 上下文需要管理这些状态信息,确保在切换策略时状态是正确的。
      • 如果策略比较多,或者策略之间的状态依赖关系复杂,上下文的代码就会变得复杂,增加了理解和维护的难度。

四、最佳实践场景

4.1 适合场景描述

  • 需要多种算法变体时 🧮
  • 需要运行时切换算法 🔄
  • 需要隔离算法逻辑与业务逻辑📦
  • 消除多个条件判断语句 🚮

4.2 具体场景

  1. 算法变体密集场景
    • 如加密算法(AES、DES)需根据不同安全级别灵活切换。
    • 支付网关:银联、支付宝、微信等支付策略。
  2. 业务规则波动频繁
    • 促销活动规则(满减、折扣、赠品等)
    • 物流配送策略(标准、特快、无人机)
  3. 复杂算法隔离需求
    • 机器学习模型(决策树、神经网络、线性回归)
    • 数据处理流程(批处理、流处理、实时处理)
  4. 多种验证方式
    • 不同用户身份验证(人脸识别、指纹识别、短信验证码等)

五、扩展

5.1 继承复用机制和复合策略

  1. 继承复用机制:可以通过共同的接口或抽象类提取公共逻辑,减少重复代码,支持策略族的复用,促进算法演进。
  2. 复合策略扩展:支持组合多个策略应对复杂场景,如组合风险评估和税务优化策略,增强系统可扩展性。

以下是一个结合继承复用复合策略扩展的C#策略模式示例,以投资决策系统场景为例:

  • 策略接口(抽象策略)

    // 策略接口(抽象策略)
    public interface IInvestmentStrategy
    {void Execute(Portfolio portfolio);
    }
    
  • 抽象策略基类(继承复用)

    • 封装公共验证逻辑ValidatePortfolio
    • 强制子类实现核心算法Execute
    • 支持统一添加日志、监控等横切关注点
    • 典型应用:所有具体策略继承公共验证和日志逻辑
    // 抽象策略基类(继承复用)
    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:风险评估策略(继承复用公共逻辑)

      // 具体策略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:税务优化策略(继承复用公共逻辑)

      
      // 具体策略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:流动性分析策略(继承复用公共逻辑)

      #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
      
  • 复合策略(策略扩展)

    • 动态组合多个策略(如风险+税务+流动性分析)
    • 支持策略执行顺序控制(通过添加顺序)
    • 允许运行时动态调整策略组合
    • 典型应用:构建投资策略流水线
    // 复合策略(策略扩展)
    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);}}
    }
    
  • 上下文类

    // 上下文类
    public class PortfolioManager
    {private IInvestmentStrategy _strategy;public void SetStrategy(IInvestmentStrategy strategy){_strategy = strategy;}public void ExecuteStrategy(Portfolio portfolio){_strategy?.Execute(portfolio);}
    }
  • Client Code 、使用示例

    
    // 使用示例
    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);

    输出

    风险评估完成,当前风险等级:Moderate
    风险评估完成,当前风险等级:Moderate
    税务优化完成,税务负担减少:15.5%
    流动性分析完成,流动性得分:0.8666666666666666666666666667
  • 关键设计说明

    • 策略族的统一管理(通过接口和抽象类)
    • 算法实现的隔离变化(具体策略独立演进)
    • 复杂业务场景的灵活组合(复合策略)
    • 公共能力的集中维护(验证、日志等)
    • 符合开闭原则
      • 新增策略只需实现IInvestmentStrategy
      • 策略演进不影响客户端代码
      • 复合策略可以嵌套其他复合策略
      • 支持策略优先级配置(通过修改复合策略实现)

5.2 对象管理:优化策略类数量增加问题优化

5.2.1 策略对象数量优化:将无状态策略设计为共享对象(使用 Flyweight 模式)

场景:假设有一个图像处理系统,支持多种图像滤镜(如黑白滤镜、模糊滤镜、锐化滤镜等)。这些滤镜是无状态的,即它们的行为不依赖于任何内部状态。

优化方式:使用 Flyweight 模式将无状态策略设计为共享对象。

  • 定义策略接口

    // 定义策略接口
    public interface IFilterStrategy
    {void ApplyFilter(string image);
    }
    
  • 具体策略类:黑白滤镜

    // 具体策略类:黑白滤镜
    public class BlackAndWhiteFilter : IFilterStrategy
    {public void ApplyFilter(string image){Console.WriteLine($"Applying Black and White filter to {image}");}
    }
    
  • 具体策略类:模糊滤镜

    // 具体策略类:模糊滤镜
    public class BlurFilter : IFilterStrategy
    {public void ApplyFilter(string image){Console.WriteLine($"Applying Blur filter to {image}");}
    }
    
  • 策略工厂类(Flyweight 模式)

    1. Flyweight 模式的核心:通过一个静态字典_filters来存储已经创建的策略对象。这样可以避免重复创建相同类型的策略对象。
    2. 单例模式的应用:由于_filters是静态的,FilterFactory类在整个程序中只会维护一份策略对象的映射关系。
    3. 策略对象的复用:当客户端请求某个类型的滤镜时,GetFilter方法会先检查_filters是否已经存在该类型的策略对象。如果不存在,则创建一个新的策略对象并存储在字典中;如果已经存在,则直接返回已有的对象。
    4. 优化效果:通过这种方式,系统中每种类型的策略对象只会被创建一次,大大减少了对象的实例化数量,节省了内存。
    // 策略工厂类(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];}
    }
    
  • 上下文类

    // 上下文类
    public class ImageProcessor
    {private IFilterStrategy _filterStrategy;public void SetFilterStrategy(IFilterStrategy filterStrategy){_filterStrategy = filterStrategy;}public void ProcessImage(string image){_filterStrategy.ApplyFilter(image);}
    }
  • Client Code

    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");}
    }
    

    结果

    Applying Black and White filter to image1.jpg
    Applying Blur filter to image2.jpg

5.2.2 上下文负责维护策略执行所需状态

场景:假设我们有一个订单处理系统,支持不同的折扣策略(如会员折扣、节日折扣等)。这些策略可能需要访问上下文中的状态(如订单金额)。

优化方式:上下文负责维护策略执行所需的状态。

  • 定义策略接口

    // 定义策略接口
    public interface IDiscountStrategy
    {decimal ApplyDiscount(decimal orderAmount);
    }
    
  • 具体策略类:会员折扣 和 具体策略类:节日折扣

    // 具体策略类:会员折扣
    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% 折扣}
    }
    
  • 上下文类

    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

    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 轻量级策略推荐单例实现,重型策略考虑对象池

场景:假设我们有一个日志系统,支持不同的日志输出策略(如控制台输出、文件输出等)。控制台输出策略是轻量级的,而文件输出策略是资源密集型的。

优化方式:轻量级策略使用单例模式,重型策略使用对象池。

  • 策略接口定义

    public interface ILogStrategy
    {void Log(string message);
    }
    
  • 具体策略类:控制台输出策略(单例模式)

    说明

    • 实现了ILogStrategy接口,用于将日志输出到控制台。
    • 使用了单例模式,确保整个程序中只有一个ConsoleLogStrategy实例。
    • Log方法将日志信息以控制台输出的形式展示。
    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}");}
    }
    
  • 具体策略类:文件输出策略

    public class FileLogStrategy : ILogStrategy
    {public void Log(string message){Console.WriteLine($"File Log: {message}");}
    }
    
  • 对象池管理文件输出策略

    说明

    • 使用对象池模式管理FileLogStrategy实例。
    • 静态构造函数初始化了对象池,预先创建了5个FileLogStrategy实例。
    • Acquire方法用于从池中获取一个实例,如果池为空,则创建新的实例。
    • Release方法将使用完毕的实例放回对象池,以便复用。
    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方法,实现日志记录。
    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实例,将日志输出到文件,并在使用完毕后将其释放回对象池。
    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);
    

    输出

    Console Log: This is a log message.
    File Log: This is a file log message.

解释ConsoleLogStrategy 使用单例模式来减少内存消耗,而 FileLogStrategy 使用对象池来管理资源密集型对象的生命周期,避免频繁的创建与销毁。

5.3 策略与上下文的通信成本优化

当不同策略需要不同参数时,上下文需处理多种参数类型,导致接口复杂且类型不安全。

5.3.1 参数封装

将参数封装到统一对象中,减少接口复杂性。

  • 参数封装类用于存储支付过程中需要的参数信息。

    说明

    此部分将支付相关的参数封装到一个类中,方便在不同支付策略中传递和使用。

    这种封装方式提高了代码的可维护性和可扩展性。

    public class PaymentParameters
    {public string CardNumber { get; set; }      // 信用卡卡号public string SecurityCode { get; set; }    // 信用卡安全码public string PhoneNumber { get; set; }     // 支付宝绑定的手机号
    }
    
  • 策略接口:IPaymentStrategy

    • 策略模式的核心是定义一个通用接口,让不同的策略类实现该接口。
    • 这里定义了一个ProcessPayment方法,所有具体的支付策略类(如信用卡支付、支付宝支付)都必须实现该方法。
    public interface IPaymentStrategy
    {void ProcessPayment(double amount, PaymentParameters parameters);  // 定义支付方法
    }
    
  • 具体策略类

    • 信用卡支付策略:CreditCardStrategy

      说明

      • 这是信用卡支付的具体实现。
      • 在支付前,会检查信用卡号和安全码是否为空。如果为空,抛出异常。
      • 如果信息完整,则输出支付信息。
      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

      说明

      • 这是支付宝支付的具体实现。
      • 在支付前,会检查手机号是否为空。如果为空,抛出异常。
      • 如果手机号有效,则输出支付信息。
      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方法,完成支付操作。
    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方法执行支付操作。
    • 通过切换策略,可以轻松地在不同支付方式之间切换,展示了策略模式的灵活性。
    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);}
    }
    

    输出

    信用卡支付:金额=100, 卡号=1234-5678-9012-3456, 安全码=123
    支付宝支付:金额=200, 手机号=13812345678

5.3.2 适配器模式

通过适配器将不同参数转换为通用格式,简化交互。

  • 通用参数接口(IPaymentParameters

    • 说明

      定义了一个通用接口,用于获取支付参数。GetParameter<T> 方法通过泛型和键值对的方式,允许不同类型的支付方式提供参数访问功能。

    public interface IPaymentParameters
    {T GetParameter<T>(string key);
    }
    
  • 参数适配器

    • 信用卡参数适配器(CreditCardAdapter

      • 功能说明
        • 信用卡支付需要提供卡号和安全码。
        • 通过 GetParameter<T> 方法,根据键值返回对应的参数。
      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> 方法,根据键值返回对应的参数。
      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 方法用于处理支付逻辑,接受金额和支付参数。
    public interface IPaymentStrategy
    {void ProcessPayment(double amount, IPaymentParameters parameters);
    }
    
  • 具体策略类

    • 信用卡支付策略(CreditCardStrategy

      • 功能说明
        • 从参数适配器中获取卡号和安全码。
        • 打印信用卡支付的详细信息。
      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

      • 功能说明
        • 从参数适配器中获取手机号。
        • 打印支付宝支付的详细信息。
      public class AlipayStrategy : IPaymentStrategy
      {public void ProcessPayment(double amount, IPaymentParameters parameters){string phone = parameters.GetParameter<string>("PhoneNumber");Console.WriteLine($"支付宝支付:金额={amount}, 手机号={phone}");}
      }
      
  • 上下文类(PaymentContext

    • 功能说明
      • 管理具体的支付策略。
      • 通过 SetStrategy 方法设置支付策略。
      • 通过 ExecutePayment 方法执行支付操作。
    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 类)

    • 功能说明
      • 创建支付上下文。
      • 设置不同的支付策略并执行支付操作。
      • 演示了如何通过策略模式切换不同的支付方式。
    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 客户端与策略解耦优化

  • 在传统的策略模式中,客户端需要了解所有策略类的具体实现细节,并负责选择合适的策略。然而,为了实现一个灵活的支付策略模式,我们可以通过解耦客户端代码与具体策略的方式,使新增支付方式时只需注册新的策略,而无需修改客户端代码。具体实现方式如下:

    1. 引入工厂模式:使用工厂模式来创建策略对象,客户端只需知道工厂接口,而无需了解具体的策略实现。
    2. 策略注册与动态选择:通过注册机制,允许策略在运行时动态选择,客户端只需提供策略标识或条件,而不需了解具体的策略实现。
    3. 使用配置文件:通过配置文件或注解来管理策略的选择,客户端可以通过配置而不是硬编码来选择策略。
  • 策略接口

    // 策略接口保持不变
    public interface IPaymentStrategy
    {void ProcessPayment(decimal amount);
    }
    
  • 策略工厂与注册中心

    策略工厂负责管理和注册具体的支付策略,提供策略的获取方式。

    说明:

    • _strategies 是一个静态字典,用于存储策略的键值对。
    • RegisterStrategy 方法用于将策略注册到工厂中,确保每个策略键是唯一的。
    • GetStrategy 方法通过键获取对应的策略实例,如果键不存在则抛出异常。
    // 策略工厂+注册中心
    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 映射到 CreditCardPaymentPayPal 映射到 PayPalPayment
    • 还定义了一个默认策略(Default),用于处理未明确指定的支付类型。
    // 配置中心(模拟appsettings.json)
    public static class AppConfig
    {public static readonly Dictionary<string, string> PaymentStrategies = new(){{ "CreditCard", "CreditCardPayment" },{ "PayPal", "PayPalPayment" },{ "Default", "CreditCard" }};
    }
    
  • 初始化注册

    在程序启动时,需要将具体的支付策略注册到工厂中。

    说明:

    • 这里注册了两种支付策略:CreditCardPaymentPayPalPayment
    • 这些策略需要实现 IPaymentStrategy 接口,并在程序启动时完成注册。
    // 初始化注册(通常在程序启动时)
    PaymentStrategyFactory.RegisterStrategy("CreditCardPayment", new CreditCardPayment());
    PaymentStrategyFactory.RegisterStrategy("PayPalPayment", new PayPalPayment());
    
  • 客户端代码 Client Code

    客户端代码实现了支付逻辑的调用,通过配置中心和策略工厂获取具体的支付策略。

    说明:

    • 客户端通过传入的 paymentType,从配置中心获取对应的策略键。
    • 如果未找到匹配的策略键,则使用默认策略。
    • 使用策略工厂获取具体的策略实例,并调用其 ProcessPayment 方法完成支付。
    // 优化后的客户端
    class OptimizedClient
    {public void Checkout(string paymentType){// 通过配置映射获取实际策略keyvar strategyKey = AppConfig.PaymentStrategies.TryGetValue(paymentType, out var key) ? key : AppConfig.PaymentStrategies["Default"];var strategy = PaymentStrategyFactory.GetStrategy(strategyKey);strategy.ProcessPayment(100);}
    }
    

    输出

    Processing 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

六、使用建议

⚠️ 注意事项:

  • 客户端需理解策略间的功能差异
  • 避免过度设计简单算法场景
  • 策略接口设计应保持适度抽象
  • 权衡模式引入的架构复杂度
  • 当存在多个相似类仅在行为不同时优先考虑
  • 配合工厂模式使用可以更好地管理策略对象
  • 注意控制策略类的数量膨胀

相关文章:

设计模式|策略模式 Strategy Pattern 详解

目录 一、策略模式概述二、策略模式的实现2.1 策略接口2.2 具体策略类2.3 上下文类2.4 客户端代码2.5 UML类图2.6 UML时序图 三、优缺点3.1 ✅优点3.2 ❌ 缺点 四、最佳实践场景4.1 适合场景描述4.2 具体场景 五、扩展5.1 继承复用机制和复合策略5.2 对象管理&#xff1a;优化策…...

Wireshark 插件开发实战指南

Wireshark 插件开发实战指南 环境搭建流程图 #mermaid-svg-XpNibno7BIyfzNn5 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-XpNibno7BIyfzNn5 .error-icon{fill:#552222;}#mermaid-svg-XpNibno7BIyfzNn5 .error-t…...

使用Java构建高效的Web服务架构

使用Java构建高效的Web服务架构 随着互联网技术的飞速发展&#xff0c;Web服务在现代应用中扮演着至关重要的角色。尤其是在企业级应用中&#xff0c;如何构建一个高效、可扩展且易维护的Web服务架构&#xff0c;成为了开发者和架构师面临的一项重要挑战。Java作为一种成熟、稳…...

《Python实战进阶》No 10:基于Flask案例的Web 安全性:防止 SQL 注入、XSS 和 CSRF 攻击

第10集&#xff1a;Web 安全性&#xff1a;防止 SQL 注入、XSS 和 CSRF 攻击 在现代 Web 开发中&#xff0c;安全性是至关重要的。无论是用户数据的保护&#xff0c;还是系统稳定性的维护&#xff0c;开发者都需要对常见的 Web 安全威胁有深刻的理解&#xff0c;并采取有效的防…...

蓝桥备赛(六)- C/C++输入输出

一、OJ题目输入情况汇总 OJ&#xff08;online judge&#xff09; 接下来会有例题 &#xff0c; 根据一下题目 &#xff0c; 对这些情况进行分析 1.1 单组测试用例 单在 --> 程序运行一次 &#xff0c; 就处理一组 练习一&#xff1a;计算 (ab)/c 的值 B2009 计算 (ab)/c …...

企微审批中MySQL字段TEXT类型被截断的排查与修复实践

在MySQL中&#xff0c;TEXT类型字段常用于存储较大的文本数据&#xff0c;但在一些应用场景中&#xff0c;当文本内容较大时&#xff0c;TEXT类型字段可能无法满足需求&#xff0c;导致数据截断或插入失败。为了避免这种问题&#xff0c;了解不同文本类型&#xff08;如TEXT、M…...

[ISP] AE 自动曝光

相机通过不同曝光参数&#xff08;档位快门时间 x 感光度 x 光圈大小&#xff09;控制进光量来完成恰当的曝光。 自动曝光流程大概分为三部分&#xff1a; 1. 测光&#xff1a;点测光、中心测光、全局测光等&#xff1b;通过调整曝光档位使sensor曝光在合理的阈值内&#xff0…...

小程序画带圆角的圆形进度条

老的API <canvas id"{{canvasId}}" canvas-id"{{canvasId}}" style"opacity: 0;" class"canvas"/> startDraw() {const { canvasId } this.dataconst query this.createSelectorQuery()query.select(#${canvasId}).bounding…...

16. LangChain实战项目2——易速鲜花内部问答系统

需求简介 易束鲜花企业内部知识库如下&#xff1a; 本实战项目设计一个内部问答系统&#xff0c;基于这些内部知识&#xff0c;回答内部员工的提问。 在前面课程的基础上&#xff0c;需要安装的依赖包如下&#xff1a; pip install docx2txt pip install qdrant-client pip i…...

代码的解读——自用

代码来自&#xff1a;https://github.com/ChuHan89/WSSS-Tissue?tabreadme-ov-file 借助了一些人工智能 run_pipeline.sh 功能总结 该脚本用于执行一个 弱监督语义分割&#xff08;WSSS&#xff09; 的完整流程&#xff0c;包含三个阶段&#xff1a; Stage1&#xff1a;训…...

蓝桥杯试题:DFS回溯

一、题目要求 输入一个数组n&#xff0c;输出1到n的全排列 二、代码展示 import java.util.*;public class ikun {static List<List<Integer>> list new ArrayList<>();public static void main(String[] args) { Scanner sc new Scanner(System.in);…...

FPGA开发,使用Deepseek V3还是R1(8):FPGA的全流程(简略版)

以下都是Deepseek生成的答案 FPGA开发&#xff0c;使用Deepseek V3还是R1&#xff08;1&#xff09;&#xff1a;应用场景 FPGA开发&#xff0c;使用Deepseek V3还是R1&#xff08;2&#xff09;&#xff1a;V3和R1的区别 FPGA开发&#xff0c;使用Deepseek V3还是R1&#x…...

一个py文件搞定mysql查询+Json转换+表数据提取+根据数据条件生成excel文件+打包运行一条龙

import os import argparse import pymssql import json import pandas as pd from datetime import datetime from pandas.io.formats.excel import ExcelFormatter import openpyxl# 投注类型映射字典 BET_MAPPING {1: WIN, 2: PLA, 3: QIN, 4: QPL,5: DBL, 6: TCE, 7: QTT,…...

微服务学习(1):RabbitMQ的安装与简单应用

目录 RabbitMQ是什么 为什么要使用RabbitMQ RabbitMQ的安装 RabbitMQ架构及其对应概念 队列的主要作用 交换机的主要作用 RabbitMQ的应用 通过控制面板操作&#xff08;实现收发消息&#xff09; RabbitMQ是什么 RabbitMQ是一个开源的消息队列软件&#xff08;消息代理…...

【RAG】Embeding 和 Rerank学习笔记

Q: 现在主流Embeding模型架构 在RAG&#xff08;Retrieval-Augmented Generation&#xff09;系统中&#xff0c;嵌入模型&#xff08;Embedding Model&#xff09; 是检索阶段的核心组件&#xff0c;负责将查询&#xff08;Query&#xff09;和文档&#xff08;Document&#…...

【Delphi】如何解决使用webView2时主界面置顶,而导致网页选择文件对话框被覆盖问题

一、问题描述&#xff1a; 在Delphi 中使用WebView2控件&#xff0c;如果预先把主界面置顶&#xff08;Self.FormStyle : fsStayOnTop;&#xff09;&#xff0c;此时&#xff0c;如果在Web页面中有使用&#xff08;<input type"file" id"fileInput" acc…...

【量化金融自学笔记】--开篇.基本术语及学习路径建议

在当今这个信息爆炸的时代&#xff0c;金融领域正经历着一场前所未有的变革。传统的金融分析方法逐渐被更加科学、精准的量化技术所取代。量化金融&#xff0c;这个曾经高不可攀的领域&#xff0c;如今正逐渐走进大众的视野。它将数学、统计学、计算机科学与金融学深度融合&…...

iOS 使用消息转发机制实现多代理功能

在iOS开发中&#xff0c;我们有时候会用到多代理功能&#xff0c;比如我们列表的埋点事件&#xff0c;需要我们在列表的某个特定的时机进行埋点上报&#xff0c;我们当然可以用最常见的做法&#xff0c;就是设置代理实现代理方法&#xff0c;然后在对应的代理方法里面进行上报&…...

16.3 LangChain Runnable 协议精要:构建高效大模型应用的核心基石

LangChain Runnable 协议精要:构建高效大模型应用的核心基石 关键词:LCEL Runnable 协议、LangChain 链式开发、自定义组件集成、流式处理优化、生产级应用设计 1. Runnable 协议设计哲学与核心接口 1.1 协议定义与类结构 #mermaid-svg-PlmvpSDrEUrUGv2p {font-family:&quo…...

Starrocks入门(二)

1、背景&#xff1a;考虑到Starrocks入门这篇文章&#xff0c;安装的是3.0.1版本的SR&#xff0c;参考&#xff1a;Starrocks入门-CSDN博客 但是官网的文档&#xff0c;没有对应3.0.x版本的资料&#xff0c;却有3.2或者3.3或者3.4或者3.1或者2.5版本的资料&#xff0c;不要用较…...

【北京迅为】itop-3568 开发板openharmony鸿蒙烧写及测试-第1章 体验OpenHarmony—烧写镜像

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…...

Electron一小时快速上手

1. 什么是 Electron? Electron 是一个跨平台桌面应用开发框架&#xff0c;开发者可以使用 HTML、CSS、JavaScript 等 Web 技术来构建桌面应用程序。它的本质是结合了 Chromium 和 Node.js&#xff0c;现在广泛用于桌面应用程序开发。例如&#xff0c;以下桌面应用都使用了 El…...

算法004——盛最多水的容器

力扣——盛最多水的容器点击即可跳转 当我们选择1号线和8号线时&#xff0c;下标为 1 和 8 形成容器的容积的高度是由 较矮的决定的&#xff0c;即下标为 8 的位置&#xff1b; 而宽度则是 1到8 之间的距离&#xff0c;为 8-17&#xff0c;此时容器的容积为 7 * 7 49。 当我…...

Java Web-Filter

Filter 在 Java Web 开发中&#xff0c;Filter&#xff08;过滤器&#xff09;是 Servlet 规范中的一个重要组件&#xff0c;它可以对客户端与服务器之间的请求和响应进行预处理和后处理。以下从多个方面详细介绍 Java Web 中的 Filter&#xff1a; 一、概念和作用 概念&…...

LeetCode 热题100 438. 找到字符串中所有字母异位词

LeetCode 热题100 | 438. 找到字符串中所有字母异位词 大家好&#xff0c;今天我们来解决一道经典的算法题——找到字符串中所有字母异位词。这道题在 LeetCode 上被标记为中等难度&#xff0c;要求我们在字符串 s 中找到所有是 p 的异位词的子串&#xff0c;并返回这些子串的…...

DeepSeek-R1训练时采用的GRPO算法数学原理及算法过程浅析

先来简单看下PPO和GRPO的区别&#xff1a; PPO&#xff1a;通过奖励和一个“评判者”模型&#xff08;critic 模型&#xff09;评估每个行为的“好坏”&#xff08;价值&#xff09;&#xff0c;然后小步调整策略&#xff0c;确保改进稳定。 GRPO&#xff1a;通过让模型自己生…...

Qt基于信号量QSemaphore实现的生产者消费者模型

在 Qt 中&#xff0c;信号量&#xff08;QSemaphore&#xff09;是一种用于控制对共享资源访问的同步工具。它允许一定数量的线程同时访问共享资源&#xff0c;适合用于生产者-消费者模型。 代码实现 #include <QCoreApplication> #include <QThread> #include &…...

七星棋牌 6 端 200 子游戏全开源修复版源码(乐豆 + 防沉迷 + 比赛场 + 控制)

七星棋牌源码 是一款运营级的棋牌产品&#xff0c;覆盖 湖南、湖北、山西、江苏、贵州 等 6 大省区&#xff0c;支持 安卓、iOS 双端&#xff0c;并且 全开源。这个版本是 修复优化后的二开版本&#xff0c;新增了 乐豆系统、比赛场模式、防沉迷机制、AI 智能控制 等功能&#…...

CSDN博客导出设置介绍

在CSDN编辑博客时&#xff0c;如果想导出保存到本地&#xff0c;可以选择导出为Markdown或者HTML格式。其中导出为HTML时有这几种选项&#xff1a;jekyll site&#xff0c;plain html&#xff0c;plain text&#xff0c;styled html&#xff0c;styled html with toc。分别是什…...

_ 为什么在python中可以当变量名

在 Python 中&#xff0c;_&#xff08;下划线&#xff09;是一个有效的变量名&#xff0c;这主要源于 Python 的命名规则和一些特殊的使用场景。以下是为什么 _ 可以作为变量名的原因和常见用途&#xff1a; --- ### 1. **Python 的命名规则** Python 允许使用字母&#xff…...