AdBlock Detected

It looks like you're using an ad-blocker!

Our team work realy hard to produce quality content on this website and we noticed you have ad-blocking enabled. Advertisements and advertising enable us to continue working and provide high-quality content.

Strategy Pattern in Java

In this post we are going to see the Strategy Pattern in Java. We can use Strategy Pattern to change behaviors at runtime.

What is the Strategy Pattern?

The Strategy Pattern is a design pattern that defines the interchange of messages between different objects to achieve a specific behavior.

Let’s imagine for a moment that we are a general from ancient Rome, planning an attack on Numancia. To do so, we need to have a clear strategy for the different situations we will encounter.

As a good general, we will define a series of behaviors for each situation, such as when arrows are shot at us or when the enemy uses cavalry or infantry.

If we translate the battlefield strategy to software development, we can see that the objective of this pattern is to define a series of behaviors to choose a specific algorithm based on a context.

Strategy Pattern in Java
Strategy Pattern

When use Strategy Pattern?

The Strategy Pattern in Java is commonly used in situations where you need to dynamically change algorithms or behaviors at runtime. Here are a few scenarios where the Strategy Pattern can be beneficial:

  1. Multiple algorithm variations: When you have a set of algorithms that can be used interchangeably based on specific conditions or requirements, the Strategy Pattern allows you to encapsulate each algorithm into separate classes. This promotes better code organization and maintainability.
  2. Runtime algorithm selection: If you need to choose an algorithm dynamically at runtime, the Strategy Pattern is an excellent choice. It allows you to switch between different strategies based on user input, configuration settings, or other runtime factors.
  3. Avoiding conditional statements: The Strategy Pattern helps you avoid large conditional statements or switch-case blocks that would otherwise be necessary to determine which algorithm to execute. Instead, you delegate the responsibility to the strategy objects themselves.
  4. Open for extension, closed for modification: The Strategy Pattern follows the Open/Closed Principle, allowing you to introduce new strategies without modifying existing code. The Strategy Pattern allows easy addition of new strategies without modifying existing code.
  5. Testing and maintenance: By encapsulating algorithms into separate classes, each strategy can be tested independently. This promotes easier unit testing and simplifies maintenance since modifications to one strategy do not affect others.

Overall, the Strategy Pattern is valuable in scenarios where you have multiple interchangeable algorithms, require runtime flexibility in choosing algorithms, and want to achieve code modularity and extensibility.

Example with Spring Boot

In this example, we will implement this pattern using Dependency Injection with Spring Boot. If you have any doubts about how Dependency Injection works in Spring, click here.

Strategy Pattern

Create the Strategy Pattern interface

The first thing we’ll do is create an interface that the rest of the classes will implement.

public interface AnimalStrategy {
    void doStuff();
    StrategyType getType();
}

Next, we define an enum class that will be used to decide which type (“getType”) we should inject.

public enum StrategyType {
   DOG,
   CAT
}

Implementation of the algorithms

Next, we’ll implement two algorithms based on whether the type is a dog or a cat:

public class IamADog  implements AnimalStrategy{
  @Override
  public void doStuff() {
      //implement algorithm Dog here 
  }
  @Override 
  public StrategyType getStrategyType() {
    return StrategyType.DOG;
  }
}
public class IamACat  implements AnimalStrategy{
  @Override
  public void doStuff() {
      //implement algorithm Dog here 
  }
  @Override 
  public StrategyType getStrategyType() {
    return StrategyType.CAT;
  }
}

We have now defined two different classes with two distinct algorithms that will be executed based on a type.

These two algorithms implement an interface with a set of common methods because the idea of this pattern is to use one or the other depending on a behavior.

Creating the factory

At this point, we’ll inject one algorithm or the other by storing the strategy type in a map, although we could use some other mechanism like a switch statement.

@Component
public class StrategyFactory {
  private Map<StrategyType, AnimalStrategy> strategies = new HashMap<StrategyType, AnimalStrategy>();
  
  public StrategyFactory(Set<AnimalStrategy> animalStrategy) {
     createStrategy(animalStrategy);
  }
  
  public AnimalStrategy findAnimalStrategy(StrategyType strategyType) {
     return strategies.get(strategyType);
  }
  private void createStrategy(Set<AnimalStrategy> animalStrategy) {
      animalStrategy.forEach( 
   strategy ->strategies.put(strategy.getStrategyType(), strategy));
 }
}

Using the Strategy Pattern

Using Spring and its annotations, we’ll inject the factory we created. In this case, we’ll inject the factory via constructor using the Lombok annotation @RequiredArgsConstructor. Here, we’ll return a Dog animal type.

@Service
@RequiredArgsConstructor
public class AnimalService {
  private final StrategyFactory strategyFactory;
  public void findAnimal(String type){
  
     if (type.equalsIgnoreCase("dog") {    
        strategyFactory.findAnimalStrategy(StrategyType.DOG);
    }
  }
}

Conclusion

In this post, we have seen the implementation of the Strategy Pattern with Spring. We can appreciate that it is one of the most useful and commonly used algorithms.

If you need more information, you can leave us a comment or send an email to refactorizando.web@gmail.com You can also contact us through our social media channels on Facebook or twitter and we will be happy to assist you!!

Leave a Reply

Your email address will not be published. Required fields are marked *