Over a million developers have joined DZone.

Null Object Pattern Implementation in Groovy

DZone's Guide to

Null Object Pattern Implementation in Groovy

· DevOps Zone
Free Resource

The Nexus Suite is uniquely architected for a DevOps native world and creates value early in the development pipeline, provides precise contextual controls at every phase, and accelerates DevOps innovation with automation you can trust. Read how in this ebook.

Simple real life problem. The proper service implementation is taken from a map based on a given service key. For an unknown/unsupported key a meaningful exception should be thrown. How could it be implemented without an if statement?

Business context. PriceProvider allows to get price for the requested product. A concrete implementation call proper provider via Web Service (REST or SOAP). There is a delegate/dispatcher which depending on a given provider name/id selects the proper implementation.

Implementation note. Mapping selected provider to the corresponding provider adapter implementation can be implemented in many ways. An external properties file, a records in a database or even have a separate web interface. However in many cases those connections are rather permanent and do not need to be changed at runtime. Then a simple map is a very compact and efficient solution. No, 6 “if..else..if..” statements are not the option :-).

There is a map with pairs: provider id -> provider service. It could be initialized (in Java) for example with:

Map<PriceProviderKey, PriceProvider> map = new EnumMap<>(PriceProviderKey);
javaMap.put(PriceProviderKey.PROVIDER1, provider1Service);
javaMap.put(PriceProviderKey.PROVIDER2, provider2Service);

On checkPrice() call proper provider service should be selected to perform a request:

// PriceProviderDelegate
public PriceResult checkPrice(PriceRequest request) {
    PriceProvider provider = map.get(request.getProvider());
    if (provider == null) { //Not very elegant
        throw new UnsupportedOperationException(
            String.format("Operator %s is not supported", request.getOperator()));
    return provider.checkPrice(request);
I don’t like ifs like that. The first idea could be an old good Null Object Pattern:

class DefaultPriceProvider implements PriceProvider {
    public PriceResult checkPrice(PriceRequest request) {
        //We don't have access to operator name which causes this exception
        throw new UnsupportedOperationException("Operator ??? is not supported");
The problem is that we need to implement all methods from the interface and what is worst we don’t know which provider id caused the situation to put it in the exception.

Then I recalled this code is in Groovy and my thoughts went to closure coercion. At the end it turned out that Groovy has a very nice mechanism for it. We can use Map.withDefault(Closure) method which allows to define logic to calculate returned value for unknown keys.

Technical insight. Under the hood Groovy creates a wrapper which for unknown keys calls the closure and put its execution result into a map (for given key). Therefor even for complicated logic its cost is paid only once – on the next query the value is got directly from a map.

Usually a constant/calculated value is returned:

def map = [a:1, b:2].withDefault{ -1 }

but in our case inside the closure we can just throw an exception:

def map = [(PriceProviderKey.PROVIDER1): provider1Service,
           (PriceProviderKey.PROVIDER2): provider2Service]
    .withDefault{ providerKey ->
            throw new UnsupportedOperationException("Provider ${providerKey} is not supported")

Then checkPrice() code can be reduced to just positive flow:

public PriceResult checkPrice(PriceRequest request) {
    def priceProvider = map.get(request.provider);

I like places when Groovy can simplify my code so much!

The DevOps Zone is brought to you in partnership with Sonatype Nexus.  See how the Nexus platform infuses precise open source component intelligence into the DevOps pipeline early, everywhere, and at scale. Read how in this ebook


Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}