Tuesday, May 23, 2023

How to implement Strategy Design Pattern in Java? (with Real World Example, Pros and Cons)

Strategy Design pattern is one of most useful versatile pattern, you will often see used in Object Oriented design. This pattern is one of the behavioral pattern mentioned by famous Gang of Four in there design pattern classics Elements of Reusable Design Pattern is Software development. As per definition goes, Strategy pattern allows you to encapsulate a set of algorithms and make them interchangeable. The best thing about Strategy pattern is that it allows you to pass a dynamic code to a method much like Lambda expression. In fact, it was one of the way to achieve same effect prior to Java 8. Also, there are multiple example of Strategy Pattern in JDK itself like Comparator and Comparable are the best example of Strategy Pattern in Java. 


Strategy Pattern in Java - A Real world Example

Look at below code for calculating postal charges, based upon weight and delivery type of Item to be posted. On surface this code looks good but it has a serious maintenance problem. A part which is very much likely to change i.e. delivery type is not properly encapsulated. This code violets Open Closed design principle, because it's not closed for modification.

In future if you need to add another delivery type say AIRMAIL, then you not only have to modify DeliveryType, but also PostageCalculator which is not obvious. This may introduce bug on a tried and tested production system. 

By using Strategy design pattern, we can solve this maintenance problem and make this code Open for extension and Closed for modification. We will introduce a PostageCalculationStrategy to calculate postage for different kinds of delivery medium.

import org.apache.log4j.Logger;

/**
  * Java program to implement Strategy design pattern. This design violets
  * Open closed design principle, and not closed for modification. By using
  * Strategy pattern, we will fix this gap.

  * @author Javin
  */
public class StrategyPatternDemo {

    private static final Logger logger = Logger.getLogger(Test.class);

    public static void main(String args[]) {
        Item gift = new Item(100);
        int postalCharge = PostageCalculator.postage(gift, DeliveryType.REGISTERED);

        System.out.printf("Weight : %dg, Delivery Type : %s, 
Postal Charges : %dc %n",
                gift.getWeight(), DeliveryType.REGISTERED, postalCharge );
     
        postalCharge = PostageCalculator.postage(gift, DeliveryType.SPEEDPOST);
     
        System.out.printf("Weight : %dg, Delivery Type : %s,
 Postal Charges : %dc %n",
                gift.getWeight(), DeliveryType.SPEEDPOST, postalCharge );
    }
}

enum DeliveryType {
    SPEEDPOST, REGISTERED, SURFACE_MAIL;
}

class Item {
    private int weight;  //in grams

    public Item(int weight) {
        this.weight = weight;
    }

    public int getWeight() {
        return weight;
    }
}

class PostageCalculator {

    public static int postage(Item item, DeliveryType deliveryType) {    
        int postage = 0;

        switch (deliveryType) {
            case SURFACE_MAIL:
                postage = item.getWeight()  1; //1 cent per gram
                break;
            case REGISTERED:
                postage = item.getWeight()  5; //5 cent per gram
                break;
            case SPEEDPOST:
                postage = item.getWeight()  8; //8 cent per gram
                break;
            default:
                throw new IllegalArgumentException("Invalid Delivery type");
        }

        return postage;
    }
}

Output
Weight : 100g, Delivery Type : REGISTERED, Postal Charges : 500c
Weight : 100g, Delivery Type : SPEEDPOST, Postal Charges : 800c

Strategy design pattern Implementation to fix Open closed violation :

import org.apache.log4j.Logger;

/**
  * Java program to implement Strategy design pattern. This design voilates
  * Open closed design principle, and not closed for modification. By using
  * Strategy pattern, we will fix this gap.
  *
  * @author
  */
public class StrategyTest {

    private static final Logger logger = Logger.getLogger(StrategyTest.class);

    public static void main(String args[]) {
        Item gift = new Item(100);
        PostageCalculator postCalc = new PostageCalculator(new SurfaceMail());
     
        int postalCharge = postCalc.postage(gift);

        System.out.printf("Weight : %dg, Delivery Type : %s, 
Postal Charges : %dc %n",
                gift.getWeight(), postCalc.getDeliveryType(), postalCharge );
     
        postCalc.setDeliveryType(new SpeedPost());
        postalCharge = postCalc.postage(gift);
     
        System.out.printf("Weight : %dg, Delivery Type : %s, 
Postal Charges : %dc %n",
                gift.getWeight(), postCalc.getDeliveryType(), postalCharge );
    }
}

class Item {
    private int weight;  //in grams

    public Item(int weight) {
        this.weight = weight;
    }

    public int getWeight() {
        return weight;
    }
}

class PostageCalculator {
    private DeliveryType deliveryType;

    public DeliveryType getDeliveryType() {
        return deliveryType;
    }

    public void setDeliveryType(DeliveryType deliveryType) {
        this.deliveryType = deliveryType;
    }
 
    public PostageCalculator(DeliveryType medium){
        this.deliveryType = medium;
    }
 

    public int postage(Item item) {    
        return item.getWeight()deliveryType.rate();

    }
}

interface DeliveryType{
    public int rate();
}

class SurfaceMail implements DeliveryType{

    @Override
    public int rate() {
        return 1;
    }

}

class RegisteredMail implements DeliveryType{

    @Override
    public int rate() {
       return 5;
    }
 
}

class SpeedPost implements DeliveryType{

    @Override
    public int rate() {
        return 8;
    }

}

Now you can see that, PostageCalculator's postage() method will not be changed, when a new delivery type will be introduced. This is the power of open closed design principle, now code is closed for modification but open for extension in form of new DeliveryType implementation.



Pros and Cons of Strategy Design pattern in Java

Here are couple of advantage and disadvantage of using Strategy Patter in Java or any Object Oriented Design.

1) One of the main advantage of Strategy Pattern is that it makes your software Open for Extension but Closed for modification by applying Open closed design principle. This means, your tried and tested code remains unchanged, when you add new functionalities to your software.

2) Another advantage of using Strategy design pattern is that, the class which uses Strategy in our example PostageCalculator is loosely coupled with different Strategy implementation e.g. Strategy which calculate postal charges for different delivery type. All it knows is that, strategy implementation implement a common Strategy interface to call the required method.

3) Strategy Pattern also promotes Single Responsibility Principle. Client class, which calculates postal charge is now independent of cost associated with different delivery types. Later if you decide to offered any discount on a particular delivery type, or a cost of a delivery type changes. 

Your change will only be limited up-to a particular DeliveryType. On the other hand if you need to do something, which is commons across all delivery type e.g. imposing a service charge, Only this class needs to be modified, without affecting how cost for different Delivery types are calculated.

4) Use of Strategy Pattern also makes testing easy. Since Client Code, which uses Strategies are only dependent on a Strategy interface and not on any particular implementation, you can test code inside Client, even if your strategy or algorithm is not ready. 

You can create a mock object or dummy implementation for your Strategy to test client code. A good example of this is Collections.sort() method, which does sorting. You can test this code by providing a mock Comparator, which defines comparison strategy.

5) Strategy Pattern greatly simplify client code, client like Sorting method can delegate comparison of object to Comparator in one line. Like in our example calculating cost of different delivery modicum is delegated.

6) One of the main advantage and difference which you seen in code before and after using Strategy pattern is elimination of conditional statements like switch and chain of if...else statements.

7) Another benefit of this pattern which comes from use of Open closed design principle is extensibility, you are free to implement as many algorithm or strategy as you need. This extensibility is really big, just imagine how many times Comparator has been implemented for different types of object.

We have seen lot of advantages and benefits of Strategy pattern in Java, now let's take a look at some of the disadvantage of using this :
1) It increases number of classes in a system by encapsulating different algorithms.
2) Client must know about different strategy available, to compose user class at runtime.

Also, here is  UML diagram of Strategy design pattern to understand the intent and structure of Strategy Design Pattern in Java:

Strategy Design Pattern in Java - Pros, Cons and Example





Things to remember about Strategy Pattern

Now, we have seen what is Strategy design Pattern, a real life example of Strategy pattern in Java and couple of pros and cons of using Strategy, it's high time to look back and revise couple of important things, which is worth remembering.

1. Strategy Pattern allows a Client to use different algorithm, based upon different types of calculation. For example a SalaryCalcuator can use different strategy to calculate salary based upon type of employee. E.g. using hourly rate for Hourly employee, daily rate for daily employee and fixed monthly salary along with allowance for full time employees.

2. Strategy Pattern ensures that your code follows Open Closed design principle, which makes it more maintainable.

3. Since Client uses Composition to use Strategy, i.e. it contains an instance variable of Strategy interface type. Client is composed with right algorithm or strategy at runtime, either using constructor or setter injection.

4. One of the best example of Strategy pattern in JDK is Comparator interface, which defines an strategy to compare object. Since different object is compared differently, Client which uses Comparator e.g. Collections.sort(), which uses Comparator for sorting purpose, is provided right Strategy at runtime. Advantage of this is that, code to sort the collection  is independent of type of object and how they compare. All it needs to know that, they implement Comparator and has compare() method.


That's all about what is Strategy pattern in Java and how to implement it. We have seen a real life example of Strategy design pattern, which fix a design which violets open closed design principle. Strategy is a very useful pattern in Object Oriented world and you might have seen a lot of use cases for it. It also promotes use of Composition, which further adds flexibility in design. Comparator class in Java is also a popular example of Strategy Pattern, where comparison strategy is changed based upon type of object.

Other Java Design Patterns tutorials you may like
  • How to implement Template Design Pattern in Java (Template Pattern)
  • 5 Free Courses to learn Object Oriented Programming (courses)
  • Difference between Factory and Dependency Injection Pattern? (answer)
  • How to design a Vending Machine in Java? (questions)
  • How to implement a Decorator design pattern in Java? (tutorial)
  • 18 Java Design Pattern Interview Questions with Answers (list)
  • When to use Command Design Pattern in Java (example)
  • 20 System Design Interview Questions (list)
  • 7 Best Courses to learn Design Pattern in Java (courses)
  • Difference between State and Strategy Design Pattern in Java? (answer)
  • 7 Books to learn System Design for Beginners (System design books)
  • How to create thread-safe Singleton in Java (example)
  • Difference between Factory and Abstract Factory Pattern? (example)
  • 5 Free Courses to learn Data Structure and Algorithms (courses)
  • How to use Factory method design pattern in Java? (tutorial)
  • 7 Best Books to learn the Design Pattern in Java? (books)
  • How to use Composite Design Pattern in Java (composite example)

Thanks a lot for reading this article so far. If you like this example and tutorial of Strategy Design Pattern in Java then please share it with your friends and colleagues. If you have any questions or feedback then please drop a note.

P. S. - If you are an experienced Java developer and  looking for a best design pattern resources like books and online courses then you can also checkout this list of best design pattern courses for experience developers to start with. It contains great online courses to learn design pattern and how to use them in real world coding. 

No comments:

Post a Comment