使用策略模式重构代码

时间:2012-07-11 14:48:55

标签: oop design-patterns domain-driven-design cqrs

我有一个GiftCouponPayment类。它有一个可以经常更改的业务策略逻辑 - GetCouponValue()。目前的逻辑是“当优惠券号码小于2000时,优惠券价值应被视为零”。在未来的业务策略中,它可能会更改为“当优惠券发行日期小于1/1/2000时,优惠券价值应被视为零”。它可以根据公司的管理部门更改为任何此类策略。

我们如何使用策略模式重构GiftCouponPayment类,以便在GetCouponValue方法的策略时不需要更改类?

更新:在分析了责任后,我觉得“GiftCoupon”将是“GiftCouponPayment”课程的更好名称。

enter image description here

C#CODE

    public int GetCouponValue()
    {
        int effectiveValue = -1;
        if (CouponNumber < 2000)
        {
            effectiveValue = 0;
        }
        else
        {
            effectiveValue = CouponValue;
        }

        return effectiveValue;
    }

阅读

  1. Strategy Pattern - multiple return types/values

4 个答案:

答案 0 :(得分:3)

GiftCouponPayment类应将GiftCoupon传递给不同的策略类。所以你的战略界面( CouponValueStrategy )应该包含一个方法:

int getCouponValue(GiftCoupon giftCoupon)

由于实施 CouponValueStrategy 的每个具体策略都可以访问GiftCoupon,因此每个策略都可以实现基于优惠券号码或优惠券日期等的算法。

答案 1 :(得分:3)

您可以在优惠券对象本身中注入“优惠券价值政策”,并要求其计算优惠券价值。在这种情况下,可以将this传递到策略中,以便策略可以向优惠券询问其所需的属性(例如优惠券号码):

public interface ICouponValuePolicy
{
  int ComputeCouponValue(GiftCouponPayment couponPayment);
}

public class GiftCouponPayment
{
  public ICouponValuePolicy CouponValuePolicy {
    get;
    set;
  }

  public int GetCouponValue()
  {
    return CouponValuePolicy.ComputeCouponValue(this);
  }
}

此外,您的GiftCouponPayment似乎对两件事(付款和礼品券)负有责任。提取包含GiftCouponCouponNumberCouponValue的{​​{1}}类可能是有意义的,并从GetCouponValue()引用此内容。

答案 2 :(得分:2)

当您的业务逻辑发生变化时,您的代码也必须更改。

您可以选择将过期检测逻辑移动到规范类中:

    public class CouponIsExpiredBasedOnNumber : ICouponIsExpiredSpecification
    {

        public bool IsExpired( Coupon c )
        {
             if( c.CouponNumber < 2000 )
                 return true;
             else
                  return false;
        }
    }

    public class CouponIsExpiredBasedOnDate : ICouponIsExpiredSpecification
    {
       public readonly DateTime expirationDate = new DateTime (2000, 1, 1);

       public bool IsExpired( Coupon c )
        {
             if( c.Date < expirationDate )
                 return true;
             else
                  return false;
        }
    }

public class Coupon
{
     public int GetCouponValue()
     {
        ICouponIsExpiredSpecification expirationRule = GetExpirationRule();

        if( expirationRule.IsExpired(this) ) 
           return 0;
        else
           return this.Value;

     }
}

你应该问自己的问题:现在是否有必要让它变得如此复杂?难道你不能让它尽可能简单地满足当前的需求,并在到期规则确实发生变化时重构它吗?

答案 3 :(得分:1)

您希望动态的行为是优惠券计算 - 可以取决于任何数量的事情:优惠券日期,优惠券号码等。我认为提供商模式更合适,注入一个服务类计算优惠券价值。

这个的本质是将业务逻辑移到GiftCouponPayment类之外,并使用我称之为“CouponCalculator”的类来封装业务逻辑。该类使用接口。

interface ICouponCalculator
{
    int Calculate (GiftCouponPayment payment);
}

public class CouponCalculator : ICouponCalculator
{
   public int Calculate (GiftCouponPayment payment)
   {
      if (payment.CouponNumber < 2000)
      {
         return 0;
      }
      else
      {
         return payment.CouponValue;
      }
   }
}

现在您已拥有此接口和类,将属性添加到GiftCouponPayment类,然后修改您的原始GetCouponValue()方法:

public class GiftCouponPayment
{
   public int CouponNumber;
   public int CouponValue;

   public ICouponCalculator Calculator { get; set; }

   public int GetCouponValue()
   {
      return Calculator.Calculate(this);
   }
}

构建GiftCouponPayment类时,您将分配Calculator属性:

var payment = new GiftCouponPayment() { Calculator = new CouponCalculator(); }
var val = payment.GetCouponValue(); // uses CouponCalculator class to get value

如果仅仅为了将计算逻辑移到GiftCouponPayment类之外,这似乎很多工作,那就是!但如果这是你的要求,它确实提供了几件事:

   1.您无需更改GiftCouponPayment类来调整计算逻辑。

   2.您可以创建实现ICalculator的其他类,以及工厂模式,以确定在构造时将哪个类注入GiftCouponPayment。这更多地说明了你对“策略”模式的最初渴望 - 因为如果逻辑变得非常复杂,这将是有用的。