首页 > 技术文章 > 使用“Empty 模式”改进 Null Object

pengzhen 2014-06-21 18:29 原文

概述

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);
    //    }
    //}
}

推荐阅读