Employee e = DB.GetEmployee(“Bob”);
If(e != null && e.IsTimeToday(today))
e.Pay();
上述代码的用法很常见,我们从数据库中查找名为Bob的员工,如果该对象不存在,返回null,如果存在,判断是它的发薪日,就调用Pay方法。&&操作可以保证仅当第一个表达式为真时才执行第二个表达式。然而有些时候我们总会忘记对null的检查而导致程序出错。有什么方法可以避免这些判断语句呢?这里可以提供一种解决方案,即NULL OBJECT模式。NULL OBJECT模式提供了当对象为空时的行为,但是它什么也没有做。灵活的使用NULL OBJECT模式可以简化我们的代码。
Employee e = DB.GetEmployee(“Bob”);
If(e != null && e.IsTimeToday(today))
e.Pay();
上述代码的用法很常见,我们从数据库中查找名为Bob的员工,如果该对象不存在,返回null,如果存在,判断是它的发薪日,就调用Pay方法。&&操作可以保证仅当第一个表达式为真时才执行第二个表达式。然而有些时候我们总会忘记对null的检查而导致程序出错。有什么方法可以避免这些判断语句呢?这里可以提供一种解决方案,即NULL OBJECT模式。NULL OBJECT模式提供了当对象为空时的行为,但是它什么也没有做。灵活的使用NULL OBJECT模式可以简化我们的代码。
对于如何理解和应用NULL OBJECT模式,以一个简单的例子来说明一下。
Code
public abstract class Book
{
public abstract bool Borrowable () ;
public abstract void Borrow();
public static readonly Book NULL;
private sealed class NullBook:Book
{
public override bool Borrowable()
{
return false;
}
public override void Borrow()
{
}
}
}
有了上面的类,我们在程序中就可以这样来应用:
Book b = DB.GetBookByID(“1000”);
If(b. Borrowable())
{
b.Borrow();
}
由上可以看出,在程序中我们可以直接使用Book的实例,而不用判断它的引用是否为空,因为无论如何,它都返回一个有效的对象。在以前的代码中,如果调用Book的地方很多,那么每个地方都会充斥着判断其引用是否为空的代码。有了NULL OBJECT我们的代码精简了不少。
在FCL中也有用NULL OBJECT模式的例子。在System.IO中关于Stream的实现中就用到了该模式,我们来看看它的实现:
Code
public abstract class Stream : MarshalByRefObject, IDisposable
{
public abstract bool CanRead { get; }
public abstract bool CanSeek { get; }
public abstract bool CanWrite { get; }
public abstract long Length { get; }
public abstract long Position { get; set; }
public abstract void Flush();
public abstract int Read([In, Out] byte[] buffer, int offset, int count);
public virtual int ReadByte();
public abstract long Seek(long offset, SeekOrigin origin);
public abstract void SetLength(long value);
[HostProtection(SecurityAction.LinkDemand, Synchronization=true)]
public static Stream Synchronized(Stream stream);
public abstract void Write(byte[] buffer, int offset, int count);
public virtual void WriteByte(byte value);
public static readonly Stream Null;
private sealed class NullStream : Stream
{
internal NullStream();
public override void Flush();
public override int Read([In, Out] byte[] buffer, int offset, int count);
public override int ReadByte();
public override long Seek(long offset, SeekOrigin origin);
public override void SetLength(long length);
public override void Write(byte[] buffer, int offset, int count);
public override void WriteByte(byte value);
// Properties
public override bool CanRead { get; }
public override bool CanSeek { get; }
public override bool CanWrite { get; }
public override long Length { get; }
public override long Position { get; set; }
}
}