factory pattern real world example in java

Factories handle the details of object creation and it encapsulate object creation.The Factory Method Pattern gives us a way to encapsulate the instantiations of concrete types and below is the official definition

The Factory Method Pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

Before we go into the Factory Method Pattern we will see a implementation of the Simple Factory which is not actually a Design Pattern; it is more of a programming idiom. But it is commonly used and they handle the details of object creation. Below is an example of the same which returns a DurationMode type of object based on the parameter we pass.


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;
}

}

The Factory Method Pattern is bit more complex than simple factory and we will see a implementation of it below.

Factory Method lets subclasses decide which class to instantiate and they say “decide” not because the pattern allows subclasses themselves to decide at runtime, but because the creator class is written without knowledge of the actual products that will be created, which is decided purely by the choice of the subclass that is used.

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 products we will be returning from the factory is of DeviationMode type and the creator classes will be BreachCalculator type. Below is the class diagram and the class hierarchy of DeviationMode is the product classes and the class hierarchy of the BreachCalculator is the creator classes.

We’ll start with the factory itself. What we’re going to do is define a class that encapsulates the object creation for all DeviationMode types . We basically have downstream and upstream types with higher and lower mode represented by the classes DownStreamHigherMode, DownStreamLowerMode, UpstreamHigherMode and the UpstreamLowerMode. We also have a UpStreamBreachCalculator and DownStreamBreachCalculator which acts like a simple factory.

The main principle behind this pattern is Depend upon abstractions and Do not depend upon concrete classes the Dependency Inversion Principle makes an even stronger statement about abstraction. It suggests that our high-level components should not depend on our low-level components rather, they should both depend on abstractions. In our case BreachCalculator is a higher level component and the deviation mode is a low-level component. This principle tells us we should write our code so that we are depending on abstractions, not concrete classes. That goes for both our high level modules and our low-level modules.

With Factory Method pattern applied our dependency is like BreachCalculator now depends on DeviationMode which is a abstract class and the concrete DeviationMode classes like DownStreamLowerMode and others also depend on the DeviationMode which is a abstract class.With Factory Method our high-level component BreachCalculator and our low-level components like DownStreamHigherMode both depend on deviation mode, the abstraction.

Lets start with the code of products which we are producing which is deviation mode.


public abstract class DeviationMode {

public abstract float calculateMeanDeviation(float rawMetricValue,
float averageMetricValue);

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

}


public class DownStreamHigherMode extends DeviationMode{

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

}


public class DownStreamLowerMode extends DeviationMode{

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

}


public class UpstreamHigherMode extends DeviationMode{

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

}


public class UpstreamLowerMode extends DeviationMode{

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

}

Lets code the creator classes


public abstract class BreachCalculator {

public abstract DeviationMode getDeviationMode(String mode);

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

DeviationMode deviationMode=getDeviationMode(mode);

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

return deviationMode.compareMeanDeviation(meanDevaition, threshold);

}

}


import java.security.InvalidParameterException;

public class DownStreamBreachCalculator extends BreachCalculator {

@Override
public DeviationMode getDeviationMode(String mode) {

DeviationMode deviationMode = null;
if (mode.equals("HIGHER")) {
deviationMode = new DownStreamHigherMode();
} else if (mode.equals("LOWER")) {
deviationMode = new DownStreamLowerMode();
} else {
throw new InvalidParameterException();
}

return deviationMode;

}

}


import java.security.InvalidParameterException;

public class UpStreamBreachCalculator extends BreachCalculator {

@Override
public DeviationMode getDeviationMode(String mode) {

DeviationMode deviationMode = null;
if (mode.equals("HIGHER")) {
deviationMode = new UpstreamHigherMode();
} else if (mode.equals("LOWER")) {
deviationMode = new UpstreamLowerMode();
} else {
throw new InvalidParameterException();
}

return deviationMode;

}

}

Lets finally code the driver class


public class Driver {

public static void main(String[] args) {

String deviationMode = "LOWER";
BreachCalculator dsbc = new DownStreamBreachCalculator();
BreachCalculator usbc = new UpStreamBreachCalculator();
boolean breach = dsbc.compare(20, 50, 30, deviationMode);
boolean breach2 = usbc.compare(20, 50, 30, deviationMode);

if (breach) {
System.out.println("breach generated");
} else {
System.out.println("No breach");
}

}
}