IMZRH的日志

努力成为一个有用的人

导航

31天重构指南之十一:使用策略类

Posted on 2009-09-29 15:39  张荣华  阅读(426)  评论(1编辑  收藏  举报

今天要说的重构没有固定的来源, 在过去的几年里我使用了该重构的几个变体,我相信这个重构肯定还有其它的变体。

这个重构一般适用的情形是当我们有许多的switch分支语句,并且经常由于有新的条件加入引起分支语句发生变化。在这种情形下我们经常引入策略模式并将每个分支条件语句定义成单独的策略类。这里我要展示的“使用策略类重构”是将上述情形重构成字典模式(策略模式的实现方式之一),这样做的好处是当您以后使用策略模式时类的使用者不需要做任何改变。

   1: namespace LosTechies.DaysOfRefactoring.SwitchToStrategy.Before
   2: {
   3:     public class ClientCode
   4:     {
   5:         public decimal CalculateShipping()
   6:         {
   7:             ShippingInfo shippingInfo = new ShippingInfo();
   8:             return shippingInfo.CalculateShippingAmount(State.Alaska);
   9:         }
  10:     }
  11:  
  12:     public enum State
  13:     {
  14:         Alaska,
  15:         NewYork,
  16:         Florida
  17:     }
  18:  
  19:     public class ShippingInfo
  20:     {
  21:         public decimal CalculateShippingAmount(State shipToState)
  22:         {
  23:             switch(shipToState)
  24:             {
  25:                 case State.Alaska:
  26:                     return GetAlaskaShippingAmount();
  27:                 case State.NewYork:
  28:                     return GetNewYorkShippingAmount();
  29:                 case State.Florida:
  30:                     return GetFloridaShippingAmount();
  31:                 default:
  32:                     return 0m;
  33:             }
  34:         }
  35:  
  36:         private decimal GetAlaskaShippingAmount()
  37:         {
  38:             return 15m;
  39:         }
  40:  
  41:         private decimal GetNewYorkShippingAmount()
  42:         {
  43:             return 10m;
  44:         }
  45:  
  46:         private decimal GetFloridaShippingAmount()
  47:         {
  48:             return 3m;
  49:         }
  50:     }
  51: }
 
为了应用“使用策略类”重构,我们将条件提取到继承自同一个接口的单独类中,然后将enum作为key参数传入。如果以后我们想添加另一个条件,我们只需要添加一个实现自接口的条件类,
并将它加入到ShippingCalculations字典中,如我之前提到的,这并不是实现策略模式的惟一方式,这样做的好处是当条件改变时ShippingInfo类的使用者不需要做任何改变,所有的改变
都仅仅发生了ShippingInfo 类内部。
Jayme Davis 指出由于绑定是通过构造函数(ctor)进行的,所以这个重构会产生更多的类,这是对的,但如果你可以应用IOC来实现IShippingCalculation模式的绑定会受益更多,这会使
你应用策略模式更简单。
   1: using System.Collections.Generic;
   2:  
   3: namespace LosTechies.DaysOfRefactoring.SwitchToStrategy.After
   4: {
   5:     public class ClientCode
   6:     {
   7:         public decimal CalculateShipping()
   8:         {
   9:             ShippingInfo shippingInfo = new ShippingInfo();
  10:             return shippingInfo.CalculateShippingAmount(State.Alaska);
  11:         }
  12:     }
  13:  
  14:     public enum State
  15:     {
  16:         Alaska,
  17:         NewYork,
  18:         Florida
  19:     }
  20:  
  21:     public class ShippingInfo
  22:     {
  23:         private IDictionary<State, IShippingCalculation> ShippingCalculations { get; set; }
  24:  
  25:         public ShippingInfo()
  26:         {
  27:             ShippingCalculations = new Dictionary<State, IShippingCalculation>
  28:             {
  29:                 { State.Alaska, new AlaskShippingCalculation() },
  30:                 { State.NewYork, new NewYorkShippingCalculation() },
  31:                 { State.Florida, new FloridaShippingCalculation() }
  32:             };
  33:         }
  34:  
  35:         public decimal CalculateShippingAmount(State shipToState)
  36:         {
  37:             return ShippingCalculations[shipToState].Calculate();
  38:         }
  39:     }
  40:  
  41:     public interface IShippingCalculation
  42:     {
  43:         decimal Calculate();
  44:     }
  45:  
  46:     public class AlaskShippingCalculation : IShippingCalculation
  47:     {
  48:         public decimal Calculate()
  49:         {
  50:             return 15m;
  51:         }
  52:     }
  53:  
  54:     public class NewYorkShippingCalculation : IShippingCalculation
  55:     {
  56:         public decimal Calculate()
  57:         {
  58:             return 10m;
  59:         }
  60:     }
  61:  
  62:     public class FloridaShippingCalculation : IShippingCalculation
  63:     {
  64:         public decimal Calculate()
  65:         {
  66:             return 3m;
  67:         }
  68:     }
  69: }
 
下面是一个当用Ninject来作为IOC容器时如何实现该重构的例子:
   1: public interface IShippingInfo
   2: {
   3:     decimal CalculateShippingAmount(State state);
   4: }
   5:  
   6: public class ClientCode
   7: {
   8:     [Inject]
   9:     public IShippingInfo ShippingInfo { get; set; }
  10:  
  11:     public decimal CalculateShipping()
  12:     {
  13:         return ShippingInfo.CalculateShippingAmount(State.Alaska);
  14:     }
  15: }
  16:  
  17: public enum State
  18: {
  19:     Alaska,
  20:     NewYork,
  21:     Florida
  22: }
  23:  
  24: public class ShippingInfo : IShippingInfo
  25: {
  26:     private IDictionary<State, IShippingCalculation> ShippingCalculations { get; set; }
  27:  
  28:     public ShippingInfo(IEnumerable<IShippingCalculation> shippingCalculations)
  29:     {
  30:         ShippingCalculations = shippingCalculations.ToDictionary(calc => calc.State);
  31:     }
  32:  
  33:     public decimal CalculateShippingAmount(State shipToState)
  34:     {
  35:         return ShippingCalculations[shipToState].Calculate();
  36:     }
  37: }
  38:  
  39: public interface IShippingCalculation
  40: {
  41:     State State { get; }
  42:     decimal Calculate();
  43: }
  44:  
  45: public class AlaskShippingCalculation : IShippingCalculation
  46: {
  47:     public State State { get { return State.Alaska; } }
  48:  
  49:     public decimal Calculate()
  50:     {
  51:         return 15m;
  52:     }
  53: }
  54:  
  55: public class NewYorkShippingCalculation : IShippingCalculation
  56: {
  57:     public State State { get { return State.NewYork; } }
  58:  
  59:     public decimal Calculate()
  60:     {
  61:         return 10m;
  62:     }
  63: }
  64:  
  65: public class FloridaShippingCalculation : IShippingCalculation
  66: {
  67:     public State State { get { return State.Florida; } }
  68:  
  69:     public decimal Calculate()
  70:     {
  71:         return 3m;
  72:     }
  73: }
原文链接:http://www.lostechies.com/blogs/sean_chambers/archive/2009/08/11/refactoring-day-11-switch-to-strategy.aspx