本文提供完整、可直接运行 的 C# 代码,展示如何用策略模式实现一个支持包裹类型、体积/重量阶梯计价、客户折扣率 的物流价格计算器,并能自动选择价格最贵的运输方案。
一、策略模式核心思想
策略模式(Strategy Pattern)定义一系列算法,将每个算法封装到独立的类中,使它们可以互相替换。
- 上下文(Context):持有一个策略对象的引用,或维护一组策略,负责调用策略的算法。
- 策略接口(IStrategy):定义所有策略必须实现的方法。
- 具体策略(ConcreteStrategy):实现策略接口,封装具体算法。
优点:
- 避免大量
if-else或switch分支。 - 符合开闭原则:增加新策略无需修改现有代码。
- 算法可复用、可独立测试。
本场景 :
物流价格 = 基础价格(重量阶梯费用 + 体积阶梯费用 + 包裹附加费)× 客户折扣率。
三种运输方式(陆运、空运、特快专递)就是三个具体策略。
上下文负责遍历所有策略,找出最终价格最高的那一个。
二、完整代码(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. 枚举定义(PackageType、CustomerType)
- 清晰表达业务中的离散状态,避免使用魔法数字。
- 便于在策略内部用
switch或if进行差异化处理。
2. 策略接口(IPricingStrategy)
- 单一职责:只负责计算价格。
- 依赖倒置:上层(上下文)依赖接口,不依赖具体类。
StrategyName属性用于友好显示。
3. 辅助类(PricingHelper)
- 集中管理阶梯规则和折扣:避免各策略重复编写相同逻辑。
- 静态方法,无状态,线程安全。
- 阶梯规则可轻易改为从配置文件或数据库读取(扩展点)。
4. 具体策略类(GroundShippingStrategy 等)
- 每个策略独立封装自己的计价系数(如空运重量单价更高)。
- 通过调用
PricingHelper获得阶梯单价和折扣,但附加费(易碎/大件)由各策略自行定义------体现策略之间的差异。 - 所有策略返回的最终价格已经过折扣处理。
5. 上下文(PricingContext)
- 策略集合:构造时注入,支持动态添加或移除策略。
- 核心方法
GetMostExpensive:- 使用 LINQ 的
Select将每个策略映射为{Strategy, Price}。 OrderByDescending(x => x.Price)按价格降序排序。First()取出最高价。
- 使用 LINQ 的
- 若需支持"最便宜"或"性价比最高",只需增加一个类似方法,改变排序规则即可。
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 中;各策略的单价系数变化被封装在各策略内部。 |
六、扩展思路(非必须,但体现设计弹性)
- 配置文件化阶梯规则 :将
GetWeightStepPrice中的阈值和单价存入 JSON 或数据库,使用策略模式加载。 - 策略工厂:结合简单工厂模式,根据用户选择的运输方式直接返回对应策略,而不用遍历所有。
- 装饰器模式:若折扣逻辑可能叠加(如 VIP 再叠加节日折扣),可使用装饰器模式包装策略。
- 策略 + 责任链:对于复合计算(先计算基础价,再应用附加费,再应用折扣),可用责任链分离各步骤。
七、结尾
本文提供了完整的、可编译运行的 C# 代码 ,并详细解释了策略模式的每一步思想。
通过这个物流价格引擎,你可以直观感受到策略模式如何让算法独立变化、如何避免条件分支,以及如何轻松扩展新算法。
在实际项目中,你可以根据业务需要调整阶梯阈值、折扣率和各策略的系数,而整体架构保持稳定。
记住:设计模式不是银弹,但在"多种算法,运行时选择"的场景下,策略模式是最自然、最优雅的解决方案。