Strategy Design Pattern in Java using Enum - Tutorial Example

I have said this before that Java Enum is very versatile and can do lot more than you normally expect from it. We have seen lot of examples of Enum in my earlier posts e.g. writing thread-safe Singleton using Enum and 10 ways to use Enum in Java. In this article, we will  learn a new way to use Enum, for implementing Strategy design pattern. Strategy pattern is one of the famous pattern, which takes advantage of polymorphism, to remove switch cases and strive for open close design principle. Formally it encapsulate related algorithm, known as strategy and make them interchangeable. So your Client, also known as Context, can use different algorithm or strategy, without any modification. One of the key advantage of Strategy pattern is it's extensibility, i.e. introducing new Strategy is as easy as writing a new class and implementing Strategy interface, with Enum, instead of creating separate class, you creates a separate Enum instance, which means less number of classes and full benefit if Strategy pattern.



How to implement Strategy pattern using Enum in Java

In this tutorial, we will see an example of implementing Strategy pattern using Java Enum, I would use a sporting example. If you are cricket fan, like me, you are going to connect with this article, but  if you are a Soccer or Tennis fan, than let me explain a bit about example.

In Cricket, there are three popular formats T20 (20 over match), One day(50 over match) and Test(5 days match). If a player, plays all three format, he needs to adjust it's batting strategy to be effective. For example, T20 is all about scoring quickly, while One day international games give bit of time for setting and then scoring. In contrast of shorter version, Test match is all about grinding i.e. occupying crease and making sure your opponent get tired, before scoring briskly.


In our Strategy pattern example, we have use an Enum to define different batting strategy. We have defined a default play() method and since, Enum can override methods, we are overriding it on every instances i.e. on T20, ONE_DAY and TEST. Our context class, which is named as Player also has a play() method, which delegates to play() method of configurable Strategy.


UML diagram of Strategy Design Pattern

Here is the UML diagram of Strategy pattern in Java, Why you should draw UML diagram? because this is the best way to remember and understand how a design pattern works. In fact, this is the standard ways to communicate your design to your fellow developers and team members.

UML diagram of Strategy Design Pattern in Java
In this diagram, I have explained strategy design pattern using sorting algorithm e.g. Bubble Sort, QuickSort, Insertion Sort  and Selection Sort. For our code example, you can replace Strategy interface with Match, and sorting strategy to T20, OneDay and Test Matches.


Strategy Pattern Example using Enum

Here is full code example of implementing Strategy design pattern using Enum in Java. If you are coding in Eclipse IDE then you don't need to do much, just select and copy this code, select the Java project you are working in Eclipse IDE and paste it. Eclipse will take care of creating right packages and source file with proper name e.g. name of public class. This is the quickest way to try and execute Java code snippet from internet in your IDE. Also remember, Strategy pattern is a good example of Open Closed Design Principle of SOLID object oriented design principle.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Java program to demonstrate that Enum can be used to implement Strategy
 * Pattern in Java.
 *
 * @author Javin
 */
public class Match {

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

    public static void main(String args[]) {
       
        Player ctx = new Player(Strategy.T20);
        ctx.play();
       
        ctx.setStrategy(Strategy.ONE_DAY);
        ctx.play();
       
        ctx.setStrategy(Strategy.TEST);
        ctx.play();
   
    }

  
}

/*
 * Player class, which uses different Strategy implementation.
 */
class Player{
    private Strategy battingStrategy;
   
    public Player(Strategy battingStrategy){
        this.battingStrategy = battingStrategy;
    }
   
    public void setStrategy(Strategy newStrategy){
        this.battingStrategy = newStrategy;
    }
   
    public void play(){
        battingStrategy.play();
    }
}

/*
 * An Enum to implement Strategy design pattern in Java. Different instances of
 * Enum represent different batting strategy, based upon type of game e.g. T20,
 * One day international or Test match.
 */
enum Strategy {

    /* Make sure to score quickly on T20 games */
    T20 {

        @Override
        public void play() {
            System.out.printf("In %s, If it's in the V, make sure it goes to tree %n", name());
        }
    },
   
    /* Make a balance between attach and defence in One day */
    ONE_DAY {

        @Override
        public void play() {
            System.out.printf("In %s, Push it for Single %n", name());
        }
    },
   
    /* Test match is all about occupying the crease and grinding opposition */
    TEST {

        @Override
        public void play() {
            System.out.printf("In %s, Grind them hard %n", name());
        }
    };

    public void play() {
        System.out.printf("In Cricket, Play as per Merit of Ball %n");
    }
}

Output:
In T20, If it's in the V, make sure it goes to tree
In ONE_DAY, Push it for Single
In TEST, Grind them hard


That's all on How to implement Strategy design pattern in Java using Enum. You can see that it's lot easier to implement Strategy pattern with Enum, you not only reduced number of classes, but also achieves extensibility and flexibility of Strategy pattern. On down side, Yes, you need to change your tried and tested Enum every time, though by inserting only new code, it's still violate Open Closed principle a bit, which advocates that new functionality should be added by writing new code, rather than modifying existing code.

5 comments :

Anonymous said...

Don't you think with this approach we are going back to if-else way. The whole purpose of Strategy pattern is to not to touch existing class and add new class (Open for extension closed for modification). With this approach we are essentially back to modifying the same class, each time I need to add extra behaviour.

Javin Paul said...

@Anonymous, you are right, it does violate the open closed design principle, because if you would like to add a new strategy let's say for 10 over match, you have to modify existing class i.e. the enum itself. So in true sense it's not really the GOF Strategy pattern, which allowed you to add new strategy without modifying existing, already tested class. This is specifically good on cases where Strategy are close related to each other and known in advance e.g. various compression strategy, various comparison strategy.

Jan nowak said...

@gusAbove: The only problem are enums residing in a single compilation unit. You edit this file, true, but no need of messing in other enum instances. Lets be honest: have you ever added new class to the project code without recompiling whole module/project? Player class doesn't change -and this is what's most important - from the client's perspective nothing changes here. Besides, making strategies enums prohibits bad - smelling extending of strategies, which is excellent. I think the overall idea is worth considering.

Anonymous said...

@Jan nowak, this still voilates open closed principle, which is the main aim of using Strategy pattern. Though its good solution in some cases but we cannot call it strategy pattern.

Flamming_Python said...

Easy to make this compatible with the Open-Close Principle really.

Just ensure that the concrete Enum implements an interface; e.g. BattingStrategy; and rename the Enum to e.g. DefaultBattingStrategies

Then change the constructor of the Player class and the setStrategy methods to take the BattingStrategy interface.

That way, there is no need to modify the Enum code at all when a new batting strategy implementation is warranted; the client can simply supply a new BattingStrategy implementation of their own, or a new Enum class which extends BattingStrategy can be created in the same package by the original developer, e.g. ExtraBattingStrategies, and with the same access permissions as DefaultBattingStrategies

Post a Comment