重构到模式(1)——宇宙大爆炸:跟If else说再见

 

今天的重构没有固定的形式,多年来我使用过不同的版本,并且我敢打赌不同的人也会有不同的版本。
该重构适用于这样的场景:switch 语句块很大,并且会随时引入新的判断条件。这时,最好使用策略模式将
每个条件封装到单独的类中。实现策略模式的方式是很多的。我在这里介绍的策略重构使用的是字典策略,
这么做的好处是调用者不必修改原来的代码。

今天的重构没有固定的形式,多年来我使用过不同的版本,并且我敢打赌不同的人也会有不同的版本。该重构适用于这样的场景:switch 语句块很大,并且会随时引入新的判断条件。这时,最好使用策略模式将每个条件封装到单独的类中。实现策略模式的方式是很多的。我在这里介绍的策略重构使用的是字典策略,这么做的好处是调用者不必修改原来的代码。

 

 

namespace LosTechies.DaysOfRefactoring.SwitchToStrategy.Before
{
public class ClientCode
{
public decimal CalculateShipping()
{
ShippingInfo shippingInfo = new ShippingInfo();
return shippingInfo.CalculateShippingAmount(State.Alaska);
}
}
public enum State
{
Alaska,
NewYork,
Florida
}
public class ShippingInfo
{
public decimal CalculateShippingAmount(State shipToState)
{
switch (shipToState)
{
case State.Alaska:
return GetAlaskaShippingAmount();
case State.NewYork:
return GetNewYorkShippingAmount();
case State.Florida:
return GetFloridaShippingAmount();
default:
return 0m;
}
}
private decimal GetAlaskaShippingAmount()
{
return 15m;
}
private decimal GetNewYorkShippingAmount()
{
return 10m;
}
private decimal GetFloridaShippingAmount()
{
return 3m;
}
}
}
要应用该重构,需将每个测试条件至于单独的类中,这些类实现了一个共同的接口。然后将枚举作为字典
的键,这样就可以获取正确的实现,并执行其代码了。以后如果希望添加新的条件,只需添加新的实现类,
并将其添加至ShippingCalculations 字典中。正如前面说过的,这不是实现策略模式的唯一方式。我在这里
将字体加粗显示,是因为肯定会有人在评论里指出这点:)用你觉得好用的方法。我用这种方式实现重构的
好处是,不用修改客户端代码。所有的修改都在ShippingInfo 类内部。
Jayme Davis指出这种重构由于仍然需要在构造函数中进行绑定,所以只不过是增加了一些类而已,但如果
绑定IShippingCalculation的策略可以置于IoC中,带来的好处还是很多的,它可以使你更灵活地捆绑策略。
 
namespace LosTechies.DaysOfRefactoring.SwitchToStrategy.After
{
public class ClientCode
{
public decimal CalculateShipping()
{
ShippingInfo shippingInfo = new ShippingInfo();
return shippingInfo.CalculateShippingAmount(State.Alaska);
}
}
public enum State
{
Alaska,
NewYork,
Florida
}
public class ShippingInfo
{
private IDictionary<State, IShippingCalculation> ShippingCalculations
{ get; set; }
public ShippingInfo()
{
ShippingCalculations = new Dictionary<State, IShippingCalculation>
{
{ State.Alaska, new AlaskShippingCalculation() },
{ State.NewYork, new NewYorkShippingCalculation() },
{ State.Florida, new FloridaShippingCalculation() }
};
}
public decimal CalculateShippingAmount(State shipToState)
{
return ShippingCalculations[shipToState].Calculate();
}
}
public interface IShippingCalculation
{
decimal Calculate();
}
public class AlaskShippingCalculation : IShippingCalculation
{
public decimal Calculate()
{
return 15m;
}
}
public class NewYorkShippingCalculation : IShippingCalculation
{
public decimal Calculate()
{
return 10m;
}
}
public class FloridaShippingCalculation : IShippingCalculation
{
public decimal Calculate()
{
return 3m;
}
}
}

 

 

 

为了使这个示例圆满,我们来看看在ShippingInfo 构造函数中使用Ninject 为IoC容器时如何进行绑定。需要

更改的地方很多,主要是将state 的枚举放在策略内部,以及Ninject 向构造函数传递一个IShippingInfo 的

IEnumerable泛型。接下来我们使用策略类中的state属性创建字典,其余部分保持不变。(感谢Nate Kohari

和Jayme Davis)

 

 

public interface IShippingInfo
{
decimal CalculateShippingAmount(State state);
}
public class ClientCode
{
[Inject]
public IShippingInfo ShippingInfo { get; set; }
public decimal CalculateShipping()
{
return ShippingInfo.CalculateShippingAmount(State.Alaska);
}
}
public enum State
{
Alaska,
NewYork,
Florida
}
public class ShippingInfo : IShippingInfo
{
private IDictionary<State, IShippingCalculation> ShippingCalculations
{ get; set; }
public ShippingInfo(IEnumerable<IShippingCalculation> shippingCalculations)
{
ShippingCalculations = shippingCalculations.ToDictionary(
calc => calc.State);
}
public decimal CalculateShippingAmount(State shipToState)
{
return ShippingCalculations[shipToState].Calculate();
}
}
public interface IShippingCalculation
{
State State { get; }
decimal Calculate();
}
public class AlaskShippingCalculation : IShippingCalculation
{
public State State { get { return State.Alaska; } }
public decimal Calculate()
{
return 15m;
}
}
public class NewYorkShippingCalculation : IShippingCalculation
{
public State State { get { return State.NewYork; } }
public decimal Calculate()
{
return 10m;
}
}
public class FloridaShippingCalculation : IShippingCalculation
{
public State State { get { return State.Florida; } }
public decimal Calculate()
{
return 3m;
}
}

 

 

 

posted @ 2010-01-28 11:41  primeli  阅读(1126)  评论(0编辑  收藏  举报