C#设计模式--策略模式(Strategy Pattern)
策略模式是一种行为设计模式,它使你能在运行时改变对象的行为。在策略模式定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相替换。通过使用策略模式,可以在运行时根据需要选择不同的算法,而不需要修改客户端代码。
主要解决的问题
解决在多种相似算法存在时,使用条件语句(如if…else)导致的复杂性和难以维护的问题。
1. 定义策略接口
public interface IStrategy
{
void Execute();
}
2. 实现具体策略
public class ConcreteStrategyA : IStrategy
{
public void Execute()
{
Console.WriteLine("Executing strategy A");
}
}
public class ConcreteStrategyB : IStrategy
{
public void Execute()
{
Console.WriteLine("Executing strategy B");
}
}
3. 上下文类
public class Context
{
private IStrategy _strategy;
public Context(IStrategy strategy)
{
_strategy = strategy;
}
public void SetStrategy(IStrategy strategy)
{
_strategy = strategy;
}
public void ExecuteStrategy()
{
_strategy.Execute();
}
}
类图
用途
策略模式主要用于以下场景:
• 算法变化:当一个类的行为或其算法需要在运行时动态改变时。
• 多个算法变体:当有多个算法变体,并且需要在运行时选择其中一个时。
• 解耦:将算法的定义与使用算法的客户端解耦。
优点
- 灵活性:可以在运行时动态切换算法。
- 扩展性:增加新的策略非常容易,只需实现策略接口即可。
- 解耦:策略类和上下文类之间松耦合,符合开闭原则。
缺点
- 客户端复杂度:客户端必须了解所有策略类的区别,以便选择合适的策略。
- 增加对象数量:每个策略都是一个类,可能会导致类的数量增加。
实际开发中的应用举例
示例1:订单处理系统中的促销策略
假设一个订单处理系统,需要根据不同的促销策略,比如:没折扣、打折扣。也可以用是会员积分兑换,来计算订单的最终价格。
1. 定义促销策略接口
public interface IPromotionStrategy
{
decimal ApplyPromotion(decimal originalPrice);
}
2. 实现具体的促销策略
public class NoDiscountStrategy : IPromotionStrategy
{
public decimal ApplyPromotion(decimal originalPrice)
{
return originalPrice; // 没有折扣
}
}
public class PercentageDiscountStrategy : IPromotionStrategy
{
private readonly decimal _discountPercentage;
public PercentageDiscountStrategy(decimal discountPercentage)
{
_discountPercentage = discountPercentage;
}
public decimal ApplyPromotion(decimal originalPrice)
{
return originalPrice * (1 - _discountPercentage / 100);//折扣
}
}
public class FixedAmountDiscountStrategy : IPromotionStrategy
{
private readonly decimal _discountAmount;
public FixedAmountDiscountStrategy(decimal discountAmount)
{
_discountAmount = discountAmount;
}
public decimal ApplyPromotion(decimal originalPrice)
{
return originalPrice - _discountAmount;
}
}
3. 上下文类
public class Order
{
private IPromotionStrategy _promotionStrategy;
private decimal _originalPrice;
public Order(decimal originalPrice, IPromotionStrategy promotionStrategy)
{
_originalPrice = originalPrice;
_promotionStrategy = promotionStrategy;
}
public void SetPromotionStrategy(IPromotionStrategy promotionStrategy)
{
_promotionStrategy = promotionStrategy;
}
public decimal CalculateFinalPrice()
{
return _promotionStrategy.ApplyPromotion(_originalPrice);
}
}
4. 使用示例
class Program
{
static void Main(string[] args)
{
// 创建订单,初始价格为100元,没有折扣
var order = new Order(100, new NoDiscountStrategy());
Console.WriteLine($"Original price: {order.CalculateFinalPrice()}");
// 应用百分比折扣策略,折扣10%
order.SetPromotionStrategy(new PercentageDiscountStrategy(10));
Console.WriteLine($"Price after 10% discount: {order.CalculateFinalPrice()}");
// 应用固定金额折扣策略,折扣20元
order.SetPromotionStrategy(new FixedAmountDiscountStrategy(20));
Console.WriteLine($"Price after fixed 20 discount: {order.CalculateFinalPrice()}");
}
}
类图:
解释
- 定义促销策略接口:IPromotionStrategy 接口定义了一个 ApplyPromotion 方法,用于计算应用促销后的价格。
- 实现具体的促销策略:
• NoDiscountStrategy:不应用任何折扣。
• PercentageDiscountStrategy:应用百分比折扣。
• FixedAmountDiscountStrategy:应用固定金额折扣。 - 上下文类:Order 类包含一个促销策略,并提供方法来设置促销策略和计算最终价格。
- 使用示例:创建一个订单,初始价格为100元,然后依次应用不同的促销策略,输出最终价格。
示例2:物流管理系统中的运输策略
假设一个物流管理系统,需要根据不同的运输方式(如快递、货运、空运等)来计算运费。也可以使用策略模式来实现这一点。
1. 定义运输策略接口
public interface ITransportStrategy
{
decimal CalculateShippingCost(decimal weight, decimal distance);
}
2. 实现具体的运输策略
public class ExpressShippingStrategy : ITransportStrategy
{
public decimal CalculateShippingCost(decimal weight, decimal distance)
{
// 快递费用计算公式:重量 * 距离 * 0.5
return weight * distance * 0.5m;
}
}
public class FreightShippingStrategy : ITransportStrategy
{
public decimal CalculateShippingCost(decimal weight, decimal distance)
{
// 货运费用计算公式:重量 * 距离 * 0.3
return weight * distance * 0.3m;
}
}
public class AirShippingStrategy : ITransportStrategy
{
public decimal CalculateShippingCost(decimal weight, decimal distance)
{
// 空运费用计算公式:重量 * 距离 * 1.0
return weight * distance * 1.0m;
}
}
3. 上下文类
public class Shipment
{
private ITransportStrategy _transportStrategy;
private decimal _weight;
private decimal _distance;
public Shipment(decimal weight, decimal distance, ITransportStrategy transportStrategy)
{
_weight = weight;
_distance = distance;
_transportStrategy = transportStrategy;
}
public void SetTransportStrategy(ITransportStrategy transportStrategy)
{
_transportStrategy = transportStrategy;
}
public decimal CalculateTotalCost()
{
return _transportStrategy.CalculateShippingCost(_weight, _distance);
}
}
4. 使用示例
class Program
{
static void Main(string[] args)
{
// 创建一个货物,重量为100公斤,距离为500公里,使用快递运输
var shipment = new Shipment(100, 500, new ExpressShippingStrategy());
Console.WriteLine($"Express Shipping Cost: {shipment.CalculateTotalCost()}");
// 更改为货运运输
shipment.SetTransportStrategy(new FreightShippingStrategy());
Console.WriteLine($"Freight Shipping Cost: {shipment.CalculateTotalCost()}");
// 更改为空运运输
shipment.SetTransportStrategy(new AirShippingStrategy());
Console.WriteLine($"Air Shipping Cost: {shipment.CalculateTotalCost()}");
}
}
类图:
解释
- 定义运输策略接口:ITransportStrategy 接口定义了一个 CalculateShippingCost 方法,用于计算运输费用。
- 实现具体的运输策略:
• ExpressShippingStrategy:快递运输费用计算。
• FreightShippingStrategy:货运运输费用计算。
• AirShippingStrategy:空运运输费用计算。 - 上下文类:Shipment 类包含一个运输策略,并提供方法来设置运输策略和计算总费用。
- 使用示例:创建一个货物,初始运输方式为快递,然后依次更改为货运和空运,输出每种运输方式的费用。
优点
- 灵活性:可以在运行时动态切换促销策略。
- 扩展性:增加新的促销策略非常容易,只需实现 IPromotionStrategy 接口即可。
- 解耦:订单类和促销策略类之间松耦合,符合开闭原则。
缺点
- 客户端复杂度:客户端必须了解所有促销策略的区别,以便选择合适的策略。
- 增加对象数量:每个促销策略都是一个类,可能会导致类的数量增加。