设计模式 学习笔记 之九
第12章 Null Object模式
在C++和Java编程中,我们经常要判断一个对象引用是否为NULL/null。只有在它不为NULL/null的前提下,我们才对它时行相应的操作。下面是分别使用Java和C++来写出的同一段逻辑。
C++ | Employee* e = DBFacade::getEmployee("Bob"); if (e != NULL && e->isTimeToPay(today)) { e->pay(); } |
Java | Employee e = DBFacade.getEmployee("Bob"); if (e != null && e.isTimeToPay(today)) { e.pay(); } |
这种对NULL/null的判断充斥在我们的代码中,看上去总是有点丑陋。更严重的问题是,有时我们会忘记检查NULL/null,从而带来程序崩溃的后果。而最严重的问题是,如果我们忘记检查NULL/null,程序并不会每次都崩溃,可能在软件开发过程中,程序都能正常运行,但是,当它真正部署到用户环境中时,却发生了崩溃!
我们可以使用Null Object模式来解决这个问题。通常这个模式会消除对NULL/null进行检查的需要,并且有助于简化代码。下图显示了这个设计。
NullEmployee类应该覆盖Employee类的所有public方法,并在这些方法中“do nothing”。这里的“do nothing”取决于Employee类的方法做了些什么。比如,isTimeToPay()方法对于NullEmployee来说就应该总是返回false,因为绝不会存在为NullEmployee支付薪水的那一天。
Null Object模式也是一个比较简单的模式,因此这里我们也重点关注它的实现。我们一般是让NullEmployee成为Employee的内部类,同时让NullEmployee的单例成为Employee类的一个常值字段NULL或一个静态方法。下表给出了Java和C++的代码。
C++ | class Employee { public: virtual bool isTimeToPay(int today) { ... do something here ... }
virtual void pay() { ... do something here ... }
static Employee* null() { class NullEmployee : public Employee { public: bool isTimeToPay(int today) { return false; }
void pay() {} };
static NullEmployee null; return (&null); } }; |
Java | public class Employee { public boolean isTimeToPay(Date today) { ... do something here ... } public void pay() { ... do something here ... }
public static final Employee NULL = new NullEmployee(); private static class NullEmployee extends Employee { @Override public boolean isTimeToPay (Date today) { return false; } @Override public void pay() {} } } |
有了Null Object,原先的代码可以写成
C++ | Employee* e = DBFacade::getEmployee("Bob"); if (e->isTimeToPay(today)) { e->pay(); } |
Java | Employee e = DBFacade.getEmployee("Bob"); if (e.isTimeToPay(today)) { e.pay(); } |
我们也可以把一个Employee对象引用与Null Object进行比较,就像下面的代码所示:
C++ | if (e == Employee::null()) { do something here } |
Java | if (e == Employee.NULL) { do something here } |
但是,由于使用Null Object模式的初衷就是为了让我们不必去把一个对象引用同NULL/null比较,因此上面的这段代码基本是不需要的。
小结
Null Object模式是一种简单但实用的模式,通过它可以消除对NULL/null的检查,这样做既简化了代码,也避免了因为忘记检查NULL/null而引发的程序异常崩溃的情况。但是使用Null Object模式也要求建立编码规范,即所有原先返回NULL/null的地方,都要用Null Object来代替。