我们常见的一个例子
Employee employee = DB.getEmployee("bob");
if(employee!=null && employee.isTimeToPay(today)){
employee.pay();
}
这个是从DB中取得名称为bob的雇员,如果取得到了,并且今天是发薪日,则会支付。我们会经常写这种判断对象为空的代码,这种写法不仅丑陋而且非常容易出错,如果我们有些地方没有判断而调用了空对象的属性或者方法,将会抛出NullpointException,那如果我们不使用这种判断而在DB的getEmployee方法中抛出异常的方式,这样讲会变得更加麻烦,我们需要把相关代码用try catch包括起来,然后还得进行处理。
这种情况我们可以使用NULL OBJECT模式来解决这些问题,通常改模式会消除对null值进行检查的需要,并且有助于代码的简化。
大致的思路是我们有两个实现了Employee接口的对象,一个是正常的EmployeeImp,另外一个是空的对象NullEmployee,当DB中取不到名字为bob的对象时,会返回NullEmployee对象。NullEmployee实现了Employee中的所有方法,但是方法中“什么也没做”,“什么也没做”的含义和具体的方法语境有关。例如,有人会期望isTimeToPay方法被实现为返回false,因为根本不会为NullEmployee支付薪水。
使用这个模式,最初的代码会更改为这样:
Employee employee = DB.getEmployee("bob");
if(employee.isTimeToPay(today)) employee.pay();
这种代码既不容易出错,也不丑陋。并且具有很好的一致性。DB.getEmployee中总会返回一个Employee的实例,不管是否找得到雇员,都可以保证返回的实例具有合适的行为。
当然,在很多情况下,仍然想要知道是否DBEmployee没有招到雇员。在Employee中创建持有唯一NullEmployee实例的static final变量就可以达到这个目的。
测试程序:
Public void testNull() throws Exception{
Employee e = DB.getEmployee(“bob”);
if(e.isTimeToPay(new Date())){
fail();
}
assertEquals(Employee.NULL,e);
}
下面程序展示了DB类。为了方便测试,getEmployee方法只返回EmployeeNULL。
Public class DB{
Public static Employee getEmployee(String name) {
Return Employee.NULL;
}
}
接下来程序就是展示Employee接口。该接口中有一个名为NULL的静态变量,持有一个匿名的实现体是无效雇员(NULL Employee)类唯一的实例,其中isTimeToPay返回false,pay方法则为空。
import java.util.Date;
/**
* Created by wangtf on 2015/12/22.
*/
public interface Employee {
public boolean isTimeToPay(Date date);
public void pay();
public static Employee NULL = new Employee() {
@Override
public boolean isTimeToPay(Date date) {
return false;
}
@Override
public void pay() {
}
};
}
使无效雇员类成为一个匿名内部类是一种确保该类只有一个单一实例的方法。实际上并不存在NullEmployee类本身。其他任何人都无法创建无效雇员的其他实例。这是非常好的,因为我们期望的表达方式是:
if(e==Employee.NULL)
如果可以创建无效雇员的多个实例,那么这种表达方式就是部可靠的