【读书活动感悟分享】—特能扛—《设计模式:可复用面向对象软件的基础》第5章 行为模式 - 5.9 STRATEGY(策略模式)_文章

【读书活动感悟分享】—特能扛—《设计模式:可复用面向对象软件的基础》第5章 行为模式 - 5.9 STRATEGY(策略模式)

王立君
发表于 2025-11-10 17:28:09

Strategy(策略模式)

策略模式是一种行为型设计模式,核心是定义一系列算法,将每个算法封装起来并使它们可相互替换。通过这种方式,算法的变化不会影响使用算法的客户端,从而实现算法的独立变化与复用。

一、核心定义与目的

  • 定义:将一组可替换的算法封装到独立的策略类中,使算法的选择和使用与客户端分离;客户端通过引用抽象策略接口,动态切换不同的算法实现,而无需修改自身代码。
  • 目的:解决算法多样性与客户端耦合的问题,使算法可以独立于使用它的客户端而变化,同时简化新增或修改算法的流程。

二、关键特性

  • 算法封装:每个算法(策略)被封装到独立的类中,专注于自身逻辑实现,符合单一职责原则。
  • 多策略可替换:所有策略类实现统一的抽象接口,客户端可在运行时无缝切换不同策略,无需修改客户端代码。
  • 客户端与策略解耦:客户端仅依赖抽象策略接口,不直接依赖具体策略类,符合依赖倒置原则。
  • 策略动态选择:可根据运行时条件(如配置、用户输入)动态选择合适的策略,提高系统灵活性。

  • 三、典型应用场景

    • 算法多样化场景:当一个问题存在多种解决方案(算法),且需要根据不同场景选择使用时(如排序算法:冒泡排序、快速排序、归并排序)。
    • 避免条件判断:当代码中存在大量基于不同条件选择算法的 if-else 或 switch-case 语句,且维护成本较高时(如折扣计算:会员折扣、节日折扣、促销折扣)。
    • 算法需要频繁切换:当系统需要在运行时动态切换算法(如出行路线规划:最快路线、最短路线、最省钱路线)。
    • 算法复用与扩展:当多个客户端需要共享同一组算法,或未来可能新增多种算法时(如支付方式:微信支付、支付宝支付、银行卡支付)。

    四、优缺点分析


    分类

    具体内容



    优点


    1. 降低耦合:客户端与具体算法解耦,算法变化不影响客户端;

    2. 增强扩展性:新增算法只需实现抽象策略接口,无需修改现有代码,符合开闭原则;

    3. 简化测试:每个策略类独立封装,可单独进行单元测试;

    4. 避免条件判断:用策略切换替代 if-else 分支,使代码更清晰、易维护;

    5. 算法复用:策略类可在多个客户端间共享,减少代码重复。



    缺点



    1. 类数量增加:每个算法对应一个策略类,策略过多时会导致系统中类的数量激增;

    2. 客户端需了解策略:客户端必须知道所有策略的存在才能选择合适的策略,增加了客户端的使用成本;

    3. 策略状态管理复杂:若策略需要维护状态,可能需要额外的机制(如上下文类)来管理,增加系统复杂度;

    4. 不适用于简单场景:若仅2-3个简单算法且无需频繁切换,使用策略模式会过度设计。

    五、核心角色与结构


    角色名称

    职责描述

    抽象策略(Strategy)

    定义所有具体策略的统一接口(或抽象类),声明策略的核心方法(算法入口);客户端通过此接口与策略交互。

    具体策略(Concrete Strategy)

    实现抽象策略接口,封装具体的算法逻辑;多个具体策略类对应不同的算法实现。

    上下文(Context)

    持有抽象策略的引用,负责策略的创建、切换和管理;提供方法供客户端设置或获取当前策略;在需要时调用当前策略的算法方法。

    客户端(Client)


    创建具体策略实例,并通过上下文设置当前策略;调用上下文的方法触发算法执行,无需直接操作具体策略。

    策略模式类图:


    客户端通过上下文设置策略,上下文将算法执行委托给当前策略;客户端可随时通过上下文切换策略。


    六、实现示例(C#)

    以“电商折扣计算”为例,系统需支持3种折扣策略:会员折扣(9折)、节日折扣(8折)、促销折扣(满1000减200),通过策略模式实现折扣算法的灵活切换。


    1. 抽象策略(Strategy)


    /// /// 抽象折扣策略接口
    /// 定义折扣计算的统一方法
    /// 
    
    public interface IDiscountStrategy
    {
        /// 
        /// 计算折扣后金额
        /// 
        /// 原价
        /// 折扣后金额
        decimal Calculate(decimal originalPrice);
    }

    2. 具体策略(Concrete Strategy)


    /// /// 具体策略1:会员折扣(9折)
    /// 
    public class MemberDiscountStrategy : IDiscountStrategy
    {
        public decimal Calculate(decimal originalPrice)
        {
            Console.WriteLine("使用会员折扣(9折)");
            return originalPrice * 0.9m;
        }
    }
    
    /// /// 具体策略2:节日折扣(8折)
    /// 
    public class HolidayDiscountStrategy : IDiscountStrategy
    {
        public decimal Calculate(decimal originalPrice)
        {
            Console.WriteLine("使用节日折扣(8折)");
            return originalPrice * 0.8m;
        }
    }
    
    /// /// 具体策略3:促销折扣(满1000减200)
    /// 
    public class PromotionDiscountStrategy : IDiscountStrategy
    {
        public decimal Calculate(decimal originalPrice)
        {
            Console.WriteLine("使用促销折扣(满1000减200)");
            return originalPrice >= 1000 ? originalPrice - 200 : originalPrice;
        }
    }

    3. 上下文(Context)


    /// /// 上下文:折扣计算器
    /// 管理策略的切换和调用
    /// 
    public class DiscountCalculator
    {
        // 持有当前策略的引用
        private IDiscountStrategy _currentStrategy;
    
        ///     /// 初始化上下文,默认使用会员折扣
        ///     public DiscountCalculator()
        {
            _currentStrategy = new MemberDiscountStrategy();
        }
    
        ///     /// 设置当前折扣策略(允许动态切换)
        ///     
        public void SetStrategy(IDiscountStrategy strategy)
        {
            _currentStrategy = strategy;
        }
    
        ///     /// 计算最终价格(委托给当前策略)
        ///     
        public decimal CalculateFinalPrice(decimal originalPrice)
        {
            return _currentStrategy.Calculate(originalPrice);
        }
    }

    4. 客户端(Client)


    /// /// 客户端:模拟不同场景下的折扣计算
    /// 
    class Program
    {
    
        static void Main(string[ ] args)
        {
            // 创建上下文(折扣计算器)
            var calculator = new DiscountCalculator();
            decimal originalPrice = 1500m;
            Console.WriteLine($"商品原价:{originalPrice:C}");
    
            // 1. 使用默认策略(会员折扣)
            decimal memberPrice = calculator.CalculateFinalPrice(originalPrice);
            Console.WriteLine($"会员折扣后价格:{memberPrice:C}");
            Console.WriteLine("------------------------");
    
            // 2. 切换为节日折扣策略
            calculator.SetStrategy(new HolidayDiscountStrategy());
            decimal holidayPrice = calculator.CalculateFinalPrice(originalPrice);
            Console.WriteLine($"节日折扣后价格:{holidayPrice:C}");
            Console.WriteLine("------------------------");
    
            // 3. 切换为促销折扣策略
            calculator.SetStrategy(new PromotionDiscountStrategy());
            decimal promotionPrice = calculator.CalculateFinalPrice(originalPrice);
            Console.WriteLine($"促销折扣后价格:{promotionPrice:C}");
        }
    }

    输出结果

    商品原价:¥1,500.00
    使用会员折扣(9折)
    会员折扣后价格:¥1,350.00
    ------------------------
    使用节日折扣(8折)
    节日折扣后价格:¥1,200.00
    ------------------------
    使用促销折扣(满1000减200)
    促销折扣后价格:¥1,300.00

    七、总结

    策略模式的核心是“算法封装与动态切换”,通过将算法与客户端分离,解决了算法多样性带来的代码臃肿问题,同时提高了系统的扩展性和可维护性。实际开发中,需根据算法数量、变化频率和切换需求决定是否使用:当算法多变且需要灵活选择时,策略模式是理想方案;当算法简单且固定时,直接实现可能更高效。


    八、培训PPT
    因无法上传附件需要跳转下方链接查看
    https://alidocs.dingtalk.com/i/nodes/wva2dxOW4kD9MK70T4ywk9dm8bkz3BRL?doc_type=wiki_doc

    79 0

    评论
    

    意见反馈