# Implement Strategy Pattern with Spring Boot

# Introduction

Strategy pattern is a behavior design pattern which allows us to select the strategy at runtime. This is particularly useful in the context where the application determines the strategy to run based on user input/selection. 

Traditionally, in a strategy pattern implementation, we would have to use a `Context` which acts as a middleman between the `client` and the `strategy executor`. However, in the case of Spring, the implementation is much simpler as there is no need for a `Context` in order for this to work, which makes the implementation much more elegant.

# Demo

In this demo, we will write a less efficient implementation first, and then attempt to enhance it.

Imagine that we have an API that provides flight information.

```java
@RestController
public class FlightController {

    @GetMapping("/flights/{airline}")
    public String getFlightInfo(@PathVariable("airline") String airline) {
        // omitted
    }
}
```

And an interface 

```java
public interface FlightInfo {
    String display();
}
```

Where different airline company would implement this interface, like such

```java
@Service
public class SingaporeAir implements FlightInfo {

    @Override
    public String display() {
        return "Singapore Airlines";
    }

}
```

Now, let's go back to our controller and finish up the implementation

```java
@RestController
@RequiredArgsConstructor
public class FlightController {
    private final SingaporeAir singaporeAir; // 1
    private final ThaiAir thaiAir; // 1

    @GetMapping("/flights/{airline}")
    public String getFlightInfo(@PathVariable("airline") String airline) {
        if ("ThaiAir".equals(airline)) { // 2
            return this.thaiAir.display(); // 3
        }

        if ("SingaporeAir".equals(airline)) { // 2
            return this.singaporeAir.display(); // 3
        }

        return "N.A";
    }
}
```

You probably will end up with something like the above, where

1. we inject the different classes that implement `FlightInfo` interface
2. match against the input
3. select the correct implementation to return the result

Let's try to hit against the API, and see the output

![curl-flights.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1654770590911/XCrFiyht_.gif align="left")

Excellent, we have done our job, and let's call it a day. But, on the next day, we need to add support to provide more airline information. With the current implementation, it's not going to be maintainable in the long run, where we would either end up with lots of `if` statements, or `switch` case statement. And that is not ideal where with each new implementation, we would need to make changes to the original code.

This is where we can enhance our current implementation.

Firstly, let's make some tweaks to some existing classes.

```java
@Service("SingaporeAir")
public class SingaporeAir implements FlightInfo {
  // omitted, no change from before
}

@Service("ThaiAir")
public class ThaiAir implements FlightInfo {
  // omitted, no change from before
}
```

We added the `beanName` so to easily match our bean against the input later on.

```java
@RestController
@RequiredArgsConstructor // 1
public class FlightController {
    private final Map<String, FlightInfo> flightInfoMap; //2

    @GetMapping("/flights/{airline}")
    public String getFlightInfo(@PathVariable("airline") String airline) {
        FlightInfo flightInfo = this.flightInfoMap.get(airline); // 3

        if (flightInfo != null) {
            return flightInfo.display(); // 4
        }

        return "N.A";
    }
}
```

1. This is a [Lombok annotation](https://projectlombok.org/features/constructor) to help create the constructor, don't worry about this. It is not critical to the implementation, and you can still use the handwritten constructor if you like
2. With [autowiring-by-type](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-autowire), `Spring` allows us to inject via a `List` or `Map`. In this case, we are using `Map` and the `key` would be the `beanName` which we declared previously
3. We assume the input would match the `beanName`, and thus, extract the correct service from the `Map`
4. And if we are able to extract, we would then call the `display` method to grab the value

Notice that now, we are only injecting a `Map` of `FlightInfo` instead of injecting the individual service(s). This way, when we introduce more airlines that implements `FlightInfo`, there would be no change to the existing code.

```java
@Service("Emirates")
public class Emirates implements FlightInfo {
  // omitted
}
```

![curl-flights-emirates.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1654773429488/Pcrc50YeB.gif align="left")

How awesome is that? 

# Conclusion

We looked at how we implemented `Strategy Pattern` with Spring Boot using a less maintainable way, and then enhanced the implementation by injecting a `Map` which allow us to look up the respective implementation and trigger the correct method call.

# Source Code

As usual, full source code is available in [GitHub](https://github.com/bwgjoseph/tutorials/tree/main/spring-boot-strategy-pattern)

# References

- [Java Design Patterns](https://java-design-patterns.com/patterns/strategy/)
