An IoC pattern to avoid repetitive if statements
Tags: cleancode, example, designpatterns, discuss
Published: 2019-12-31 05:30:10

---
title: An IoC pattern to avoid repetitive if statements
published: true
description: A pattern that avoids repetitive if statements 
tags: clean code, example, design patterns, discuss
---
![Clean factory](https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcQ9ndTpjh9i2J9-ASUgUm80Fo95T1ax82ctvG6c9x-EHuMGFXQv)

In my [previous post](https://dev.to/tomazlemos/keeping-your-code-clean-by-sweeping-out-if-statements-4in8) I shared two patterns that helped avoiding repetitive if statements, and we had a great discussion about imperative, if-based styles and declarative ones.

In this post I want to share another pattern which I think is great to expand our programming toolset.

I'll address some of the tradeoffs I see in it against the more traditional if-based approach, and I'd really like you to tag along and share what pros and cons you see in them in the comments. Let's do this together?

The examples are in Java but it should translate well to any other OO language.

_If you prefer, skip right to [the code](#thecode)_

###The pattern

This pattern is based on Java's Spring framework, and it's main point is that it offloads from the main class to the instances themselves the responsibility of knowing whether or not it is capable of, or should, handle a specific input, thus inverting the flow control.

It's widely used by Spring to find out which converter or mapper to use for a given input. Part of its value derives from being able to use unknown custom implementations of the interfaces provided by the user, but I do find value in using it for known types as well.

###The context

For context, let's say we have an application that has to handle lots of different kinds of data, based on a what's the data's url’s country passed as a path parameter. Something like `https://dev.to/myapp/data?url=http://www.google.com.br` (I didn't escape the path param's url to make it clearer).

The application's job would be to retrieve the IncomingData from the url, and then pass it to the proper DataHandler provided by the Selector.

We might have a DataHandler interface that would be implemented by many classes, such as BrazilianDataHandler, SpanishDataHandler, FrenchDataHandler, etc, and a Selector to provide the correct instances.

Looking at the code should make it more clear, so let's do just that.
<a name="thecode"></a>

###The usual if-based approach

Let’s start with an interface such as:

```java
public interface DataHandler {
    ProcessedData handleData(IncomingData data);
}
```

A concrete implementation of the interface might look like:

```java
// Other countries' handlers could be implemented the same way
public class BrazilianDataHandler implements DataHandler {
    @Override
    public ProcessedData handleData(IncomingData data) {
        // do stuff;
    }
}
```

Now let’s add the if-based selector:

```java
public class DataHandlerSelector {

    private final DataHandler brazilianDataHandler;
    private final DataHandler argentinianDataHandler;
    private final DataHandler australianDataHandler;
    private final DataHandler frenchDataHandler;
    // Lots of other countries...

    public DataHandlerSelector(BrazilianDataHandler brazilianDataHandler,
                                        ArgentinianDataHandler argentinianDataHandler,
                                        AustralianDataHandler australianDataHandler,
                                        FrenchDataHandler frenchDataHandler) {

        this.brazilianDataHandler = brazilianDataHandler;
        this.argentinianDataHandler = argentinianDataHandler;
        this.australianDataHandler = australianDataHandler;
        this.frenchDataHandler = frenchDataHandler;
    }

    public DataHandler obtainDataHandlerFor(String url) {
        Assert.notNull(url, () -> "Url is null!");

        DataHandler dataHandlerToReturn;

        if (url.contains(".br")) {
            dataHandlerToReturn = brazilianDataHandler;
        } else if (url.contains(".ar")) {
            dataHandlerToReturn = argentinianDataHandler;
        } else if (url.contains(".au")) {
            dataHandlerToReturn = australianDataHandler;
        } else if (url.contains(".fr")) {
            dataHandlerToReturn = frenchDataHandler;
        // Lots of other countries...
        } else {
            throw new IllegalArgumentException("Url not supported: " + url);
        }

        return dataHandlerToReturn;
    }

    private static class Assert {
        public static void notNull(Object input, Supplier<String> errorMessageSupplier) {
            if (input == null) throw new IllegalArgumentException(errorMessageSupplier.get());
        }
    }
}
```

And then a factory to instantiate the selector and it’s dependencies:

```java
public class DataHandlerSelectorFactory {

   public static DataHandlerSelector createSelector() {
       return new DataHandlerSelector(new BrazilianDataHandler(), new ArgentinianDataHandler(), new AustralianDataHandler(), new FrenchDataHandler());
   }
}
```

Simple, right? This pattern has the great benefit of having a straightforward logic, which often leads to easier reasoning as it's a very common pattern.

There are some drawbacks I see, tough:

* The Selector logic has to be modified every time we need to include another country
* Lots of language specific words and symbols obscuring the business logic, which would be the condition - handler correlation
* It tends to get huge over time, with lots of copying and pasting, possibly leading to bugs and poorer comprehensibility and maintainability
* It is a single point of failure, meaning if some logic gets messed up you can mess several or all countries at once (think if you accidentally delete an "else if" line and the line above it...)
* You may need to see both the Selector and the implementation class to get the big picture about what the implementation class does

What do you think, do you agree with these points?

###The IoC Selector pattern

In this Selector pattern the interface gets a new shouldHandle method:

```java
public interface DataHandler {
    ProcessedData handleData(IncomingData data);
    boolean shouldHandle(String url);
}
```

And the Selector becomes:

```java
public class DataHandlerSelector {

    private final List<DataHandler> dataHandlerList;

    public DataHandlerSelector(DataHandler... dataHandlers) {
        dataHandlerList = Arrays.asList(dataHandlers);
    }

    public DataHandler obtainDataHandlerFor(String url) {
        Assert.notNull(url, () -> "Url is null!");
        return dataHandlerList
                .stream()
                .filter(handler -> handler.shouldHandle(url))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("Url not supported: " + url));

    // You can also check if more than one instance has been found by using List.size() on the list returned by a collect(toList()) at the end of the stream call.
    }

    private static class Assert {
        public static void notNull(Object input, Supplier<String> errorMessageSupplier) {
            if (input == null) throw new IllegalArgumentException(errorMessageSupplier.get());
        }
    }
}
```

Let's take a look at a sample implementation of the new interface:

```java
public class BrazilianDataHandler implements DataHandler {
    @Override
    public ProcessedData handleData(IncomingData data) {
        // do stuff;
    }

    @Override
    public boolean shouldHandle(String url) {
        return url.contains(".br");
    }
}
```

In this example we have changed the centralized approach of the first Selector into a decentralized approach, handing down to the implementations the responsibility of knowing whether or not it should act on a given input.

Overall, I like designs that forces us to make our intentions clear and makes it easy to figure out which are the business rules and exceptional cases.

I think this approach brings as advantages:

* No modification needed in the Selector class when adding a new implementation
* The condition and the logic itself are together, allowing for better clarity and cohesion
* No cluttered language specific words clouding business rules
* No copying and pasting
* No single point of failure

And as drawbacks:
* We loose sight of the greater picture of conditions
* May take longer to reason about the first time you see it
* One condition may interfere with the other if it is greedy and comes before the other

What about you, can you see any other advantages or drawbacks?

###The wicked new requirement

Ok, now a new requirement has arrived, and there are some classes that will have to be verified first. 

In this case we will have the .gov and .org urls that have to be addressed by special handlers regardless of their countries (I know that doesn't make that much sense but just bear with me 😄).

The if-based Selector's implementation might be something like:

```java
public class DataHandlerSelector {

    // Constructor, properties and Assert class omitted for brevity

    public DataHandler obtainDataHandlerFor(String url) {
        Assert.notNull(url, () -> "Url is null!");

        DataHandler dataHandlerToReturn;

        // .gov and .org must come first
        if (url.contains(".gov")) {
            dataHandlerToReturn = governmentDataHandler;
        } else if (url.contains(".org")) {
            dataHandlerToReturn = organizationDataHandler
        } else if (url.contains(".br")) {
            dataHandlerToReturn = brazilianDataHandler;
        } else if (url.contains(".ar")) {
            dataHandlerToReturn = argentinianDataHandler;
        } else if (url.contains(".au")) {
            dataHandlerToReturn = australianDataHandler;
        } else if (url.contains(".fr")) {
            dataHandlerToReturn = frenchDataHandler;
        // Lots of other countries...
        } else {
            throw new IllegalArgumentException("Url not supported!");
        }

        return dataHandlerToReturn;
    }
}
```

Easy to implement right? But the way I see it, in this case the new added business rule is enforced only by that comment, which may or may not stand the test of time. We might be able to extract a method to put the first two ones in a different place but I do begin to smell spaghetti... We can discuss better solutions for this pattern in the comments if you have any.

And what about with the IoC Selector pattern?

Well, we might just create two lists:

```java

public class DataHandlerSelector {

   private final List<DataHandler> firstHandlersList;
   private final List<DataHandler> secondHandlersList;

   public DataHandlerSelector(GovernmentDataHandler governmentDataHandler,
                              OrganizationDataHandler organisationDataHandler,
                              BrazilianDataHandler brazilianDataHandler,
                              ArgentinianDataHandler argentinianDataHandler,
                              AustralianDataHandler australianDataHandler,
                              FrenchDataHandler frenchDataHandler) {

        firstHandlersList = List.of(governmentDataHandler,
                                  organisationDataHandler);

        secondHandlersList = List.of(brazilianDataHandler,
                                  argentinianDataHandler,
                                  australianDataHandler,
                                  frenchDataHandler);


    public DataHandler obtainDataHandlerFor(String url) {
        Assert.notNull(url, () -> "Url is null!");
        return firstHandlersList
                .stream()
                .filter(handler -> handler.shouldHandle(url))
                .findFirst()
                .orElseGet(() -> getFromSecondHandlerList(url));
    }

    private DataHandler getFromSecondHandlerList(String url) {
        return secondHandlersList
                           .stream()
                           .filter(handler -> handler.shouldHandle(url))
                           .findFirst()
                           .orElseThrow(() -> new IllegalArgumentException("Url not supported!"));
    }

    private static class Assert {
        public static void notNull(Object input, Supplier<String> errorMessageSupplier) {
            if (input == null) throw new IllegalArgumentException(errorMessageSupplier.get());
        }
    }
}
```

It sure is easier to code the first pattern, but in which do you think the new business rule is more clear? In my option, dealing with well reasoned patterns and interfaces usually leads to more reflection than action, and thus probably to better, more maintainable code, and with a clearer set of business rules and responsibilities.

But, of course, every pattern has its day of glory when applied in the proper situation, right?

So, what do you think are the pros and cons of each pattern? What points I made do you agree or disagree with? Is there another requirement change we should compare the two patterns against?

Let's address this in the comments!

_Thanks a lot to @wrldwzrd89, @khtony, @bertilmuth and @t_dardzhonov for the early feedback on this post!_