strategy design pattern real world example in java

The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

The principle behind the strategy design pattern is to identify the aspects of your application that vary and separate them from what stays the same.Basically we need to take the parts that vary and encapsulate them, so that later you can alter or extend the parts that vary without affecting those that do not change. We will be using composition to implement this design pattern as composition gives you a lot more flexibility. Not only does it let you encapsulate a family of algorithms into their own set of classes, but it also lets you change behavior at runtime as long as the object you’re composing with implements the correct behaviour interface.

Problem

Lets say we have a data comparison use case and we need to compare the raw data with the aggregated data based on the deviation mode that will be configured in the database and also we need to have separate algorithm based on the duration mode that will be set in the database.

The deviation behaviors will be LowerDeviationMode and HigherDeviationMode and the duration behaviour will be PreviousWeekDurationMode and SameWeekDurationMode. Notice also that we have started to describe things a little differently. Instead of thinking of the deviation and duration behaviors as a set of behaviors, we will start thinking of them as a family of algorithms.

Below is the structure which we will be implementing for strategy design pattern . DurationMode and Deviation Mode are the interface or the behaviour and LowerDeviationMode,HigherDeviationMode,PreviousWeekDurationMode and SameWeekDurationMode are the family of algorithms.

The key is that a DataComparator will now delegate its DeviationMode and DurationMode behaviour. First we will add two instance variables to the DataComparator class called durationMode and deviationMode, that are declared as the interface type. Each DataComparator object will set these variables polymorphically to reference the specific behaviour type it would like at runtime. We have two constructor one which requires the behaviour to be passed during the instantiation and the other which sets default behaviour in the constructor and we do not need to pass the behaviour at creation time. We can also set the behaviour dynamically using the setter methods.

The Strategy Pattern uses the composition which is a HAS-A relationship. Here the DataComparator has a DeviationMode and a DurationMode to which it delegates DeviationMode and DurationMode behaviour. Creating systems using composition gives you a lot more flexibility. Not only does it let you encapsulate a family of algorithms into their own set of classes, but it also lets you change behaviour at runtime.

Lets start by coding the behaviours first

Lets define the DeviationMode interface


public interface DeviationMode {

public float calculateMeanDeviation(float rawMetricValue,float averageMetricValue);

public boolean compareMeanDeviation(float meanDevaition,int threshold);

}

HigherDeviationMode which implements the DeviationMode has a algorithm to calculate the mean and to check the mean against a threshold.


public class HigherDeviationMode implements DeviationMode {

@Override
public float calculateMeanDeviation(float rawMetricValue, float averageMetricValue) {
if (rawMetricValue > averageMetricValue) {
float meanDeviation = ((rawMetricValue - averageMetricValue) / averageMetricValue) * 100;
return meanDeviation;
}
return 0;
}

@Override
public boolean compareMeanDeviation(float meanDevaition, int threshold) {
return meanDevaition > threshold;
}

}

LowerDeviationMode which implements the DeviationMode has a algorithm to calculate the mean and to check the mean against a threshold.


public class LowerDeviationMode implements DeviationMode {

@Override
public float calculateMeanDeviation(float rawMetricValue, float averageMetricValue) {
if ( averageMetricValue > rawMetricValue) {
float meanDeviation = ((averageMetricValue - rawMetricValue) / averageMetricValue) * 100;
return meanDeviation;
}
return 0;
}

@Override
public boolean compareMeanDeviation(float meanDevaition, int threshold) {
return meanDevaition > threshold;
}

}

Lets define the DurationMode interface


public interface DurationMode {

public List<String> getTimeStamps(String timeStamp, String duration);

}

PreviousWeekDurationMode which implements the DurationMode has a algorithm to return a list of timestamp which will be used to check breach . I have removed the actual logic to shorten the code and make it simple as we are focussing mainly on the design pattern.

public class PreviousWeekDurationMode implements DurationMode {

@Override
public List<String> getTimeStamps(String timeStamp, String duration) {

// logic to return timestamps
List<String> time = new ArrayList<String>();
time.add("LastWeekDate");
return time;

}

}

SameWeekDurationMode which implements the DurationMode has a algorithm to return a list of timestamp which will be used to check breach . I have removed the actual logic to shorten the code and make it simple as we are focussing mainly on the design pattern.


import java.util.ArrayList;
import java.util.List;

public class SameWeekDurationMode implements DurationMode {

@Override
public List<String> getTimeStamps(String timeStamp, String duration) {

List<String> time = new ArrayList<String>();
time.add("TodayDate");

return time;

}

}

We will also be using the enums DurationModeType and DeviationModeType to make sure the configured value in the db is as per the enum values.


public enum DeviationModeType {

LOWER,HIGHER;

}


public enum DurationModeType {

SW, PW

}

We will also be using DeviationModeFactory and the DurationModeFactory which is basically an implementation of the factory design pattern and which will return the appropriate behaviours based on the configuration.


import java.security.InvalidParameterException;

public class DeviationModeFactory {

public static DeviationMode getDeviationMode(DeviationModeType deviationModeType) {

DeviationMode deviationMode = null;
if (deviationModeType.equals(DeviationModeType.HIGHER)) {
deviationMode = new HigherDeviationMode();
} else if (deviationModeType.equals(DeviationModeType.LOWER)) {
deviationMode = new LowerDeviationMode();
} else {
throw new InvalidParameterException();
}
return deviationMode;
}

}


import java.security.InvalidParameterException;

public class DurationModeFactory {

public static DurationMode getDurationMode(DurationModeType durationModeType) {

DurationMode durationMode = null;
if (durationModeType.equals(DurationModeType.PW)) {
durationMode = new PreviousWeekDurationMode();
} else if (durationModeType.equals(DurationModeType.SW)) {
durationMode = new SameWeekDurationMode();
} else {
throw new InvalidParameterException();
}
return durationMode;
}

}

DataComparator class which has the DeviationMode and DurationMode behaviours .


import java.util.List;

public class DataComparator {

private DeviationMode deviationMode;

private DurationMode durationMode;

public DataComparator(DeviationMode deviationMode, DurationMode durationMode) {
super();
this.deviationMode = deviationMode;
this.durationMode = durationMode;
}

public DataComparator() {
super();
this.deviationMode = new LowerDeviationMode();
this.durationMode = new SameWeekDurationMode();
}

public DeviationMode getDeviationMode() {
return deviationMode;
}

public void setDeviationMode(DeviationMode deviationMode) {
this.deviationMode = deviationMode;
}

public DurationMode getDurationMode() {
return durationMode;
}

public void setDurationMode(DurationMode durationMode) {
this.durationMode = durationMode;
}

public boolean compare(String duration, String timestamp, int rawdata, int aggregateData, int threshold) {

List<String> time = durationMode.getTimeStamps(timestamp, duration);

float meanDevaition = deviationMode.calculateMeanDeviation(rawdata, aggregateData);

return deviationMode.compareMeanDeviation(meanDevaition, threshold) && time.contains("TodayDate");

}

}

 

And finally we have a driver class where we are setting the deviationMode and durationMode behaviour dynamically based on the configuration.


public class Driver {

public static void main(String[] args) {

String deviationMode = "LOWER";
String duration = "5";
String durationType = "SW";
DeviationModeType deviationModeType = DeviationModeType.valueOf(deviationMode.toUpperCase());
DeviationMode deviationModeStrategy = DeviationModeFactory.getDeviationMode(deviationModeType);
DurationModeType durationModeType = DurationModeType.valueOf(durationType.toUpperCase());
DurationMode durationModeStrategy = DurationModeFactory.getDurationMode(durationModeType);
DataComparator dataComparator = new DataComparator();
dataComparator.setDeviationMode(deviationModeStrategy);
dataComparator.setDurationMode(durationModeStrategy);

if (dataComparator.compare(duration, "time", 2, 25, 80)) {
System.out.println("breach");
} else {
System.out.println("no breach");
}

}

}

 

Thats all for strategy design pattern hope it was usefull.