使用“Empty 模式”改进 Null Object
概述
Null Object 是Martin 大师提出的一种重构手段,其思想就是通过多态(派生一个Null对象)来减少逻辑(if … then …else)的判断。
而.NET中已经有Null Object 的使用原型了——“类型.Empty”。
//1) String.Empty //2) return Enumerable.Empty<ModelValidationResult>(); //3) return GetRouteDataValue<IEnumerable<RouteData>>(routeData, RouteDataTokenKeys.DirectRouteMatches) ?? Enumerable.Empty<RouteData>();
下面我们就来实现一个Empty。
使用Empty
1)抽取一个接口INullable
理由:仿照《重构》的例子,同时通过一个IsNull来体现是否为空对象。
public interface INullable { bool IsNull(); }
2)定义Payer对象
描述:一个支付对象,拥有一个Pay的方法,进行商品的支付。
同时,其实现了Empty模式,代表Null Object。
NewNull方法为一个参考(和原书上讲的对比),可以直接去掉。
public class Payer : INullable { #region 实现Null Object 模式 private static readonly Payer _Empty = new NullPayer(); public static Payer Empty { get { return _Empty; } } public static Payer NewNull() { return new NullPayer(); } #endregion /// <summary> /// 支付 /// </summary> /// <param name="money"></param> public virtual void Pay(decimal money) { // do pay Console.WriteLine(string.Format("支付:{0}元!", money)); } public virtual bool IsNull() { return false; } }
3)定义NullPayer,
描述:为了去除“if (payer == null )”,用一个NullPayer进行替换。
实际上就是多态的体现
/// <summary> /// 空对象 /// </summary> public class NullPayer : Payer { public override bool IsNull() { return true; } public override void Pay(decimal money) { //skip do nothing } }
4)测试
简单的测试,将Empty方式推向到可以应用的层面。感谢Martin大师!!!
[TestFixture] public class PayerTests { #region INullable接口测试 [Test] public void InstanceOfPayerWillBeNotNull() { var payer = new Payer(); Assert.False(payer.IsNull()); } [Test] public void InstanceOfNullPayerWillBeNull() { var payer = new NullPayer(); Assert.True(payer.IsNull()); } #endregion [Test] public void EmptyIsInstancOfNullPayer() { var payer = Payer.Empty; Assert.IsInstanceOf<NullPayer>(payer); } [Test] public void UserEmptyForPayDoNothing() { var payer = DbMethodGetEmptyPayer(); payer.Pay(100); payer = DbMethodGetNotNullPayer(); payer.Pay(100); } private Payer DbMethodGetEmptyPayer() { return Payer.Empty; } private Payer DbMethodGetNotNullPayer() { return new Payer(); } //public void OrigenCode() //{ // var pay = new Payer(); // if (pay != null) // { // pay.Pay(150); // } //} }