Click here to Skip to main content
15,891,136 members
Articles / Programming Languages / Java / Java SE

Implementing the Null Object Pattern with a proxy

Rate me:
Please Sign up or sign in to vote.
4.50/5 (6 votes)
16 Feb 2009Apache5 min read 28.2K   263   8  
This article shows how a Null Object can be dynamically generated with a proxy.

Introduction and motivation

Each Java programmer must have experienced, countless times, a system crash or a similar problem caused by a frustrating NullPointerException. To protect our software against this problem and make it safer, it is common for us to fill our code with checks that prevent null variables from being called, forcing us to translate a trivial method like this:

Java
public String getProjectManagerName() {
    return getProject().getManager().getName();
}

to this:

Java
public String getProjectManagerName() {
    Project project = getProject();
    if (project == null) return "";
    Manager manager = project.getManager();
    if (manager == null) return "";
    return manager.getName();
}

This approach works fine if you have to use it in just one or two places, but if you need to repeat it across all your software, it dramatically worsens its readability and extensibility by bloating it with unnecessary code. Moreover, this kind of null logic fails to provide protection for new code, so if programmers forget to include it, the same problem can occur again.

This problem is so important to drive in a more modern language like Groovy the introduction of the safe-dereference operator (?.). Using this construct, the former method can be safely rewritten as follows:

Java
public String getProjectManagerName() {
    return getProject()?.getManager()?.getName();
}

How the Null Object pattern solves the problem

While the Java language does not provide a safe-dereference operator, it is still possible to achieve the same result by using the so called Null Object pattern. According to Wikipedia:

“A Null Object is an object with defined neutral ("null") behavior.”

In other words, a Null Object of a given class has a type compatible with that class (by extending it or implementing a common interface), and provides a default (“null”) behavior in the absence of a more meaningful one. For example, the Null Object version of the classes Project and Manager involved in the former code snippets can be implemented as follows:

Java
public class NullProject extends Project { 
    public Manager getManager() {
        return new NullManager();
    } 
}
Java
public class NullManager extends Manager {
    public String getName() {
        return "";
    }
}

Note that, of course, a Null Object must always returns another Null Object in order to keep an invocation sequence safe. In this way (provided that the getProject() method will return an instance of NullProject when it should return just a null), the first version of our getProjectManagerName() becomes safe, again eliminating the need for all the checks introduced in the second version.

In my opinion, despite this solution being effective and allowing to write cleaner code, it has a major defect: it obliges us to implement, maintain, and test a Null Object version of each class of our domain model. And again, it doesn’t offer protection for new code in a more subtle way: if a programmer adds a new method to a domain object, forgetting to override it in its Null Object version, you will be exposed to the risk of a NullPointerException.

Implementing the Null Object pattern with a proxy

In order to remove these last issues, it would be better to dynamically generate the Null Object classes through a proxy instead of statically implementing them. To investigate this last solution, I implemented a class called NullObjectProxy that is attached to this article, together with a test class that also better illustrates how to use it. The core of this proxy is, of course, in its invoke() method that defines the actions it performs when an invocation on a Null Object is intercepted.

Java
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
    String methodName = method.getName(); 
    if (methodName.equals("getNullClass") && 
       (args == null || args.length == 0)) return clazz;     

    // it delegates the hashCode and the equals methods to the proxy class
    if (methodName.equals("hashCode") && 
               (args == null || args.length == 0))
        return clazz.hashCode(); 
    if (methodName.equals("equals") && args.length == 1 
                   && args[0] instanceof NullObject) 
        return ((NullObject)args[0]).getNullClass() == clazz;     

    // it checks if a special return value has been defined for this method call
    Method mockedMethod = getMockedMethod(method);
    if (mockedMethod != null) return mockedMethodValuesMap.get(mockedMethod);    

    Class<?> returnedType = method.getReturnType();
    if (returnedType == Void.TYPE) return null;
    
    // if the returned type is an interface
    // it returns a Null Object for that interface
    if (returnedType.isInterface()) return nullObjectOf(returnedType);          

    // it checks if has been defined a default value for this returned type
    try {
        return NullObjectProxy.class.getMethod("null" + 
          returnedType.getSimpleName() + "Value").invoke(NullObjectProxy.class);
    } catch (Exception e) { }

    // it tries to instance an object of the given return
    // type but invoking its empty constructor (if any)
    try {
        return returnedType.newInstance();
    } catch (Exception e) { }      

    // if all the former strategies fail just return null
    return null;
}

This class allows to dynamically instantiate a proxy that implements the Null Object pattern for any given interface by passing the class of the interface to be mocked to the static constructor:

Java
public static <T> T nullObjectOf(Class<T> clazz);

This proxy guarantees the safety of any invocation sequence because each method call to it returns (when possible) another Null Object, that in turn mocks the type returned by the method itself. In this way, to make safe the original non-checked implementation of the getProjectManagerName() method, it is enough to return nullObjectOf(Project.class) from the getProject() call when it should actually return a null.

To resume, this solution allows to use the Null Object pattern, freeing programmers from the burden of writing a specific Null Object implementation for each of the classes of the domain model. Note that this implementation uses the standard proxy mechanism provided by the Java language, so it currently works only with interfaces, but it is easy to modify it to make it work with any non-final class by using the cglib library. In each case, you could not implement the Null Object pattern for a final class since, by definition, you are not allowed to extend it.

As stated, when the declared type returned by the invoked method is not an interface, the Null Object proxy is not able to generate another Null Object. In this case, the proxy tries to return a meaningful value, applying two strategies. First, it checks if there is a predefined default return value compatible with the requested one, and in particular, it returns 0 for any Number, false for the boolean, a whitespace for a char, and an empty String and a Date representing the current system time for the String and the Date, respectively. Then, if none of the above applies, it tries to instantiate an object of the given return type, but invoking its empty constructor, if any. In the end, if both these strategies fail, the only thing it can do to avoid a ClassCastException is to return a null.

Despite those default returned values generally being correct in 9 cases out of 10, sometimes, they can be formally wrong. For example, while it makes sense that a Null Object Collection returns 0 when the size() method is invoked on it, it sounds at least odd that it returns false on a call to its isEmpty() method. Addressing this last issue has been made possible by override the default behavior for a specific method by calling setMockedMethod(). For example, to instruct the proxy to return true when isEmpty() is called on a Null Object Collection, it is sufficient to reconfigure it by calling:

Java
setMockedMethod(Collection.class, "isEmpty", true);

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0


Written By
Switzerland Switzerland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --