Posts Tagged ‘groovy’

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 old good Null Object Pattern:

class DefaultPriceProvider implements PriceProvider {
    @Override
    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);
    priceProvider.checkPrice(request);
}

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

Groovy logo

Advertisements

Spock is a testing framework for Java and Groovy applications. It allows to write tests in highly expressive specification language which under the hood leverages Groovy compile-time metaprogramming capabilities (especially AST transformations).

Spock developers prefer flat test code formatting with empty lines:

class OrderedInteractionsSpec extends Specification {
  def "collaborators must be invoked in order"() {
    def coll1 = Mock(Collaborator)
    def coll2 = Mock(Collaborator)

    when:
    // try to reverse the order of these invocations and see what happens
    coll1.collaborate()
    coll2.collaborate()

    then:
    1 * coll1.collaborate()

    then:
    1 * coll2.collaborate()
  }
}

I, in turn, prefer more vertically compact formatting without empty lines, but with intend statements after label:

class OrderedInteractionsSpec extends Specification {
    def "collaborators must be invoked in order"() {
        given:
            def coll1 = Mock(Collaborator)
            def coll2 = Mock(Collaborator)
        when:
            // try to reverse the order of these invocations and see what happens
            coll1.collaborate()
            coll2.collaborate()
        then:
            1 * coll1.collaborate()
        then:
            1 * coll2.collaborate()
    }
}

Unfortunately my formatting style was not supported in IntelliJ Idea, even with idea-spock-enhancements plugin. This forced people like me to format it manually and pay attention to automatic code reformat.

Idea 13 provides a lot of new features and improvements. One of them is:

Label Formatting
Code style settings now let you specify custom indentation for the label blocks.

I have to admit I treated it as something not very important and until some time later when a colleague asked me if I know that feature I realized this is something I wanted.

With a simple configuration change Idea 13+ can be configured to support compact Spock specification formatting with code indentation. In File -> Settings (ALT-CTRL-S) -> Code Style -> Groovy set Label indent to 4 (or 2 if preferred) and Label indent style to Indent statements after label.

Spock test code formatting in Idea13

That is all. ALT-CTRL-L (Reformat code) becomes your friend again.

Btw, one more thing pointed out by @mariusz_s. By default labels in Idea looks like ordinal text. In Spock tests labels are part of the specification language and it would be nice to stand them out. It is possible with Spock Enhancements plugin for Idea which make the labels bold and colored.

Label highlighting with Spock plugin

The plugin provides also live inspections for block ordering errors which is very handy and I suggest it to all writing tests in Spock. Code long and prosper ;)