Strategy pattern - Java - Explained

complete guide with intent, motivation, trade-offs, and different implementation styles

Strategy pattern - Java - Explained

Intent

Define a family of algorithms, encapsulate each one and make them interchangeable. Strategy lets the algorithm vary independently from clients that use them. Strategy pattern is also known as policy.
It's a type of Behavioral design pattern.

Motivation behind the pattern/problem it solves?

When an OO client class has more than one behavior (methods) that somewhat belongs to a similar category/cause but has different logic (algorithm) then it's advised to encapsulate those behaviors together in a strategy so that a client class can select and change behavior dynamically at runtime.

It's useful in scenarios where we want to **provide different logic for different behaviors **in the class but which belong to similar intent.

In the example below, we have different logic (behavior) for calculating shipping costs based on country code but all these behaviors belong to the same intent which is to calculate shipping costs.

design problem scenario 1 - strategy pattern

Problem Statement :

We have a requirement to include a feature to calculate shipping charges for different countries. Different countries have different 3rd party system which provides the shipping cost for that specific country.

psudo code 
calculateShippingCharges(String coutry, int totalItemsInCart, double totalWeight) {

if(country == "US") {
// call to some external system 
getShippingChargesForUS(totalItemsInCart, totalWeight);

}
if(country == "CA") {
// call to some external system 
getShippingChargesForCA(totalItemsInCart, totalWeight);
}
}

approach: without using strategy pattern.

Without using strategy pattern github link for code

image.png

click to open in new tab : withoutstrategyexample.jpg

What is the problem with this approach and how strategy pattern will help us to solve it?

The service class is going to be a huge class with additional countries which will be difficult to maintain, also there will be multiple if-else involved to select exact logic which is also a concern.

approach: using a simple strategy pattern.

Strategy pattern helps to extract logic which intended for the same cause but has different business logic, to encapsulate those in separate dedicated classes which we call a strategy. github link

image.png

click to open in new tab - simplestrategyusinginstancevariable.jpg

Strategy pattern help us to separate business logic into separate classes (strategies) which can be changed at runtime as well.

Two ways to implement a strategy pattern :

1) using instance variable github link

In this, we associate strategy with context with an instance variable .

2) using method arguments github link

In this, we associate strategy with context class by passing as a method argument.

validation strategy pattern

Can you think a scenario where you want to pass a specific object through various validations? but you would want to inject validations dynamically at runtime? you would wish to keep each validation logic in separate classes? then think about the validation strategy pattern.
github code

Example : ValidationStrategyPatternMain.java : client class

public class ValidationStrategyPatternMain {

    public static void main(String[] args) {


        String couponCode1 = "AggA";
        String couponCode2 = "OFFER";

        List<ValidateCouponStrategy> couponStrategies = new ArrayList<>();
        couponStrategies.add(new SpecialCharactersValidateCouponStrategy());
        couponStrategies.add(new UppercaseValidateCouponStrategy());
        couponStrategies.add(new NoWhiteSpaceValidateCouponStrategy());

        // iterate over all validation rules for a coupon

        boolean isValidCoupon = couponStrategies.stream().allMatch(strategy -> strategy.validate(couponCode1));

        System.out.println("The coupon "+couponCode1+" is valid ? "+isValidCoupon);
    }
}

Strategies :

public interface ValidateCouponStrategy {

    boolean validate(String couponCode);
}

****
public class SpecialCharactersValidateCouponStrategy implements ValidateCouponStrategy {

    Pattern my_pattern = Pattern.compile("[^a-z0-9 ]", Pattern.CASE_INSENSITIVE);

    @Override
    public boolean validate(String couponCode) {
        Matcher my_match = my_pattern.matcher(couponCode);
        return !my_match.find();
    }
}

***
public class NoWhiteSpaceValidateCouponStrategy implements ValidateCouponStrategy{
    @Override
    public boolean validate(String couponCode) {
        return !StringUtils.containsWhitespace(couponCode);
    }
}

***
public class UppercaseValidateCouponStrategy implements ValidateCouponStrategy {

    @Override
    public boolean validate(String couponCode) {
        return couponCode.equals(couponCode.toUpperCase());
    }
}

Structure: strategy pattern

Client code, context, and strategies are the main components in the strategy pattern structure.

image.png

Click on this image name to open in new tab strategypatternstructure.jpg

Context class :

This class maintains the reference to the concrete strategies where its declaration is an interface but the actual reference to the strategy is the concrete strategy

Strategy

Interface common to all strategies

Concrete Strategies

Implementation, which is actually different variations of the algorithms used by the context.

In short, client code creates the instance of concrete strategy, and context class has a setter that helps to associate a specific concrete strategy with context class at runtime.

Applicability

  1. Strategy pattern provides a way to configure a class with one of the many behaviors which can be configured at the runtime. Here many behaviors are referred to as many different algorithms which intended for a similar cause but have different business logic.

  2. The Strategy pattern lets you indirectly alter the object’s behavior at runtime by associating it with different sub-objects which can perform specific sub-tasks in different ways.

  3. The Strategy pattern lets you extract the varying behavior into a separate class hierarchy and combine the original classes into one, thereby reducing duplicate code.

  4. When you have multiple if-else conditions to select specific algorithms consider choosing a strategy pattern.

Pros and Cons

Pros

  1. We can swap the algorithm associated with the context class at the runtime.

  2. helps replace inheritance with composition.

  3. Provides the abstraction in the context class which helps hide implementation details from the code which uses them.

  4. Open/Closed Principle. You can introduce new strategies without having to change the context.

Cons

  1. When we have limited algorithms that rarely change then it's not needed to make it complicated with strategy.

  2. Client has to have the ability to understand and visibility to choose the correct strategy to associate with the context class.

Relations with other patterns

  1. Bridge, State, Strategy have very similar structures, all of these patterns are based on composition, which is delegating work to other objects. However, their intent is different and problem they all solve is different.

  2. Command and Strategy may look similar because you can use both to parameterize an object with some action. However, they have very different intents.

  3. Decorator vs Strategy
    Decorator lets you change the skin of an object, while Strategy lets you change the guts.

  4. Strategy vs Template
    Template Method is based on inheritance: it lets you alter parts of an algorithm by extending those parts in subclasses.
    Strategy is based on composition: you can alter parts of the object’s behavior by supplying it with different strategies that correspond to that behavior.

  5. State vs Strategy
    The state can be considered as an extension of Strategy. Both patterns are based on composition: they change the behavior of the context by delegating some work to helper objects. Strategy makes these objects completely independent and unaware of each other. However, State doesn’t restrict dependencies between concrete states, letting them alter the state of the context at will.

please #### click here for the complete code on github.

Thanks for reading !!! Love IT Live IT Enjoy IT !!!! Happy Coding !!!