策略模式完整实现:物流价格计算引擎

本文提供完整、可直接运行 的 C# 代码,展示如何用策略模式实现一个支持包裹类型、体积/重量阶梯计价、客户折扣率 的物流价格计算器,并能自动选择价格最贵的运输方案。


一、策略模式核心思想

策略模式(Strategy Pattern)定义一系列算法,将每个算法封装到独立的类中,使它们可以互相替换。

  • 上下文(Context):持有一个策略对象的引用,或维护一组策略,负责调用策略的算法。
  • 策略接口(IStrategy):定义所有策略必须实现的方法。
  • 具体策略(ConcreteStrategy):实现策略接口,封装具体算法。

优点

  • 避免大量 if-elseswitch 分支。
  • 符合开闭原则:增加新策略无需修改现有代码。
  • 算法可复用、可独立测试。

本场景

物流价格 = 基础价格(重量阶梯费用 + 体积阶梯费用 + 包裹附加费)× 客户折扣率。

三种运输方式(陆运、空运、特快专递)就是三个具体策略。

上下文负责遍历所有策略,找出最终价格最高的那一个。


二、完整代码(C# 控制台应用程序)

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Linq;

namespace StrategyPatternLogistics
{
    // ======================== 枚举定义 ========================
    /// <summary>
    /// 包裹类型
    /// </summary>
    public enum PackageType
    {
        Regular,   // 普通包裹
        Fragile,   // 易碎包裹
        Bulky      // 大件包裹
    }

    /// <summary>
    /// 客户类型
    /// </summary>
    public enum CustomerType
    {
        Regular,     // 普通客户
        VIP,         // VIP客户
        Enterprise   // 企业客户
    }

    // ======================== 策略接口 ========================
    /// <summary>
    /// 计价策略接口
    /// </summary>
    public interface IPricingStrategy
    {
        string StrategyName { get; }
        decimal CalculatePrice(PackageType type, double volume, double weight, CustomerType customer);
    }

    // ======================== 辅助类:阶梯计价与折扣 ========================
    /// <summary>
    /// 提供阶梯单价和折扣率的静态方法
    /// </summary>
    public static class PricingHelper
    {
        /// <summary>
        /// 根据重量(kg)返回阶梯单价(元/kg)
        /// </summary>
        public static decimal GetWeightStepPrice(double weight)
        {
            if (weight <= 5)
                return 2.0m;
            if (weight <= 10)
                return 1.8m;
            return 1.5m;
        }

        /// <summary>
        /// 根据体积(m³)返回阶梯单价(元/m³)
        /// </summary>
        public static decimal GetVolumeStepPrice(double volume)
        {
            if (volume <= 0.5)
                return 10.0m;
            if (volume <= 1.0)
                return 8.0m;
            return 6.0m;
        }

        /// <summary>
        /// 根据客户类型返回折扣率
        /// </summary>
        public static decimal GetCustomerDiscount(CustomerType customer)
        {
            switch (customer)
            {
                case CustomerType.VIP:
                    return 0.85m;       // 85折
                case CustomerType.Enterprise:
                    return 0.75m;       // 75折
                default:
                    return 1.0m;        // 无折扣
            }
        }
    }

    // ======================== 具体策略1:陆运 ========================
    public class GroundShippingStrategy : IPricingStrategy
    {
        public string StrategyName => "陆运";

        public decimal CalculatePrice(PackageType type, double volume, double weight, CustomerType customer)
        {
            // 1. 阶梯计价:重量费用 + 体积费用
            decimal weightCost = PricingHelper.GetWeightStepPrice(weight) * (decimal)weight;
            decimal volumeCost = PricingHelper.GetVolumeStepPrice(volume) * (decimal)volume;
            decimal basePrice = weightCost + volumeCost;

            // 2. 包裹类型附加费
            if (type == PackageType.Fragile)
                basePrice += 10m;
            if (type == PackageType.Bulky)
                basePrice += 8m;

            // 3. 应用客户折扣
            decimal discount = PricingHelper.GetCustomerDiscount(customer);
            return basePrice * discount;
        }
    }

    // ======================== 具体策略2:空运 ========================
    public class AirShippingStrategy : IPricingStrategy
    {
        public string StrategyName => "空运";

        public decimal CalculatePrice(PackageType type, double volume, double weight, CustomerType customer)
        {
            // 空运的阶梯单价更高
            decimal weightCost;
            if (weight <= 5)
                weightCost = (decimal)weight * 5.0m;
            else if (weight <= 10)
                weightCost = (decimal)weight * 4.5m;
            else
                weightCost = (decimal)weight * 4.0m;

            decimal volumeCost;
            if (volume <= 0.5)
                volumeCost = (decimal)volume * 15.0m;
            else if (volume <= 1.0)
                volumeCost = (decimal)volume * 12.0m;
            else
                volumeCost = (decimal)volume * 10.0m;

            decimal basePrice = weightCost + volumeCost;

            if (type == PackageType.Fragile)
                basePrice += 20m;
            if (type == PackageType.Bulky)
                basePrice += 15m;

            decimal discount = PricingHelper.GetCustomerDiscount(customer);
            return basePrice * discount;
        }
    }

    // ======================== 具体策略3:特快专递 ========================
    public class ExpressShippingStrategy : IPricingStrategy
    {
        public string StrategyName => "特快专递";

        public decimal CalculatePrice(PackageType type, double volume, double weight, CustomerType customer)
        {
            // 特快专递:固定高单价 + 加急费
            decimal weightCost = (decimal)weight * 8.0m;
            decimal volumeCost = (decimal)volume * 20.0m;
            decimal basePrice = weightCost + volumeCost + 30m;  // 30元加急费

            if (type == PackageType.Fragile)
                basePrice += 15m;
            if (type == PackageType.Bulky)
                basePrice += 20m;

            decimal discount = PricingHelper.GetCustomerDiscount(customer);
            return basePrice * discount;
        }
    }

    // ======================== 上下文:管理策略并选出最贵方案 ========================
    public class PricingContext
    {
        private readonly List<IPricingStrategy> _strategies;

        public PricingContext(List<IPricingStrategy> strategies)
        {
            _strategies = strategies ?? throw new ArgumentNullException(nameof(strategies));
        }

        /// <summary>
        /// 遍历所有策略,返回价格最贵的方案(价格 + 策略名称)
        /// </summary>
        public (decimal Price, string StrategyName) GetMostExpensive(PackageType type, double volume, double weight, CustomerType customer)
        {
            if (_strategies.Count == 0)
                throw new InvalidOperationException("未注册任何计价策略。");

            var best = _strategies
                .Select(s => new
                {
                    Strategy = s,
                    Price = s.CalculatePrice(type, volume, weight, customer)
                })
                .OrderByDescending(x => x.Price)
                .First();

            return (best.Price, best.Strategy.StrategyName);
        }
    }

    // ======================== 客户端程序入口 ========================
    class Program
    {
        static void Main(string[] args)
        {
            Console.OutputEncoding = System.Text.Encoding.UTF8;

            // 1. 注册所有可用的计价策略
            List<IPricingStrategy> strategies = new List<IPricingStrategy>
            {
                new GroundShippingStrategy(),
                new AirShippingStrategy(),
                new ExpressShippingStrategy()
            };

            // 2. 创建上下文
            PricingContext context = new PricingContext(strategies);

            // 3. 用户输入
            Console.WriteLine("========== 物流价格计算(自动选择最贵方式) ==========");
            Console.Write("请输入包裹类型 (0=普通, 1=易碎, 2=大件): ");
            int typeInput = int.Parse(Console.ReadLine());
            PackageType packageType = (PackageType)typeInput;

            Console.Write("请输入体积(立方米,例如 0.6): ");
            double volume = double.Parse(Console.ReadLine());

            Console.Write("请输入重量(千克,例如 2.5): ");
            double weight = double.Parse(Console.ReadLine());

            Console.Write("请输入客户类型 (0=普通, 1=VIP, 2=企业): ");
            int custInput = int.Parse(Console.ReadLine());
            CustomerType customer = (CustomerType)custInput;

            // 4. 计算最贵方案
            var (price, strategyName) = context.GetMostExpensive(packageType, volume, weight, customer);

            // 5. 输出结果
            Console.WriteLine($"\n推荐最贵的物流方式: {strategyName}");
            Console.WriteLine($"最终价格: {price:C2}");

            Console.WriteLine("\n按任意键退出...");
            Console.ReadKey();
        }
    }
}

三、代码逐层思想解析

1. 枚举定义(PackageTypeCustomerType

  • 清晰表达业务中的离散状态,避免使用魔法数字。
  • 便于在策略内部用 switchif 进行差异化处理。

2. 策略接口(IPricingStrategy

  • 单一职责:只负责计算价格。
  • 依赖倒置:上层(上下文)依赖接口,不依赖具体类。
  • StrategyName 属性用于友好显示。

3. 辅助类(PricingHelper

  • 集中管理阶梯规则和折扣:避免各策略重复编写相同逻辑。
  • 静态方法,无状态,线程安全。
  • 阶梯规则可轻易改为从配置文件或数据库读取(扩展点)。

4. 具体策略类(GroundShippingStrategy 等)

  • 每个策略独立封装自己的计价系数(如空运重量单价更高)。
  • 通过调用 PricingHelper 获得阶梯单价和折扣,但附加费(易碎/大件)由各策略自行定义------体现策略之间的差异。
  • 所有策略返回的最终价格已经过折扣处理。

5. 上下文(PricingContext

  • 策略集合:构造时注入,支持动态添加或移除策略。
  • 核心方法 GetMostExpensive
    • 使用 LINQ 的 Select 将每个策略映射为 {Strategy, Price}
    • OrderByDescending(x => x.Price) 按价格降序排序。
    • First() 取出最高价。
  • 若需支持"最便宜"或"性价比最高",只需增加一个类似方法,改变排序规则即可。

6. 客户端(Program.Main

  • 组装策略列表 → 创建上下文 → 获取用户输入 → 调用上下文 → 输出结果。
  • 完全不知道内部如何计算,只依赖上下文提供的接口。

四、运行示例

复制代码
========== 物流价格计算(自动选择最贵方式) ==========
请输入包裹类型 (0=普通, 1=易碎, 2=大件): 1
请输入体积(立方米,例如 0.6): 0.6
请输入重量(千克,例如 2.5): 2.5
请输入客户类型 (0=普通, 1=VIP, 2=企业): 2

推荐最贵的物流方式: 特快专递
最终价格: ¥66.00

五、设计思想总结表

设计原则/思想 在本代码中的体现
开闭原则 新增运输方式(如海运)只需添加新的策略类,无需修改现有策略和上下文。
单一职责原则 每个策略只负责一种运输方式的计价;PricingHelper 只负责阶梯和折扣;上下文只负责选择最贵方案。
依赖倒置原则 上下文依赖 IPricingStrategy 接口,而非具体类。
组合优于继承 上下文通过组合多个策略来获得不同算法,而不是通过继承产生爆炸式的子类。
策略模式 算法族(陆运、空运、特快)被封装成独立类,可互换。
封装变化 阶梯规则、折扣率的变化被封装在 PricingHelper 中;各策略的单价系数变化被封装在各策略内部。

六、扩展思路(非必须,但体现设计弹性)

  1. 配置文件化阶梯规则 :将 GetWeightStepPrice 中的阈值和单价存入 JSON 或数据库,使用策略模式加载。
  2. 策略工厂:结合简单工厂模式,根据用户选择的运输方式直接返回对应策略,而不用遍历所有。
  3. 装饰器模式:若折扣逻辑可能叠加(如 VIP 再叠加节日折扣),可使用装饰器模式包装策略。
  4. 策略 + 责任链:对于复合计算(先计算基础价,再应用附加费,再应用折扣),可用责任链分离各步骤。

七、结尾

本文提供了完整的、可编译运行的 C# 代码 ,并详细解释了策略模式的每一步思想。

通过这个物流价格引擎,你可以直观感受到策略模式如何让算法独立变化、如何避免条件分支,以及如何轻松扩展新算法。

在实际项目中,你可以根据业务需要调整阶梯阈值、折扣率和各策略的系数,而整体架构保持稳定。

记住:设计模式不是银弹,但在"多种算法,运行时选择"的场景下,策略模式是最自然、最优雅的解决方案。

相关推荐
x-cmd1 天前
macOS 内存模型深度解析 | x free 设计哲学
linux·macos·内存·策略模式·free·x-cmd
互联网散修1 天前
零基础鸿蒙应用开发第二十九节:策略模式重构电商促销系统
重构·策略模式·鸿蒙零基础入门
无籽西瓜a1 天前
【西瓜带你学设计模式 | 第十五期 - 策略模式】策略模式 —— 算法封装与动态替换实现、优缺点与适用场景
java·后端·设计模式·软件工程·策略模式
互联网散修2 天前
零基础鸿蒙应用开发第二十八节:商品排序体系之工厂与策略模式
策略模式·鸿蒙
stevenzqzq2 天前
架构设计深度解析:策略模式 + 抽象工厂在UI适配中的高级应用
ui·策略模式
tiger从容淡定是人生6 天前
可审计性:AI时代自动化测试的核心指标
人工智能·自动化·项目管理·策略模式·可用性测试·coo
都说名字长不会被发现7 天前
模版方法 + 策略模式在库存增加/扣减场景下的应用
策略模式·模板方法模式·宏命令·策略聚合·库存设计
默|笙7 天前
【Linux】进程概念与控制(2)_进程控制
java·linux·策略模式
枫叶林FYL8 天前
Agent/Teakenote 系统(Swarm 架构)深度技术报告
架构·策略模式