template pattern java real world example

The Template Method Pattern is a behavioral design pattern which defines the skeleton of an algorithm in a method, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithms structure.

This pattern is used to create a template for an algorithm which is basically a method which specifically has a method that defines an algorithm as a set of steps. One or more of these steps is defined to be abstract and implemented by a subclass. This ensures the algorithms structure stays unchanged, while subclasses provide some part of the implementation.

We can also add hook which is a method that is declared in the abstract class with only empty or default implementation. This gives subclasses the ability to hook into the algorithm at various points but a subclass is also free to ignore the hook.

The template pattern is also used to implement the Hollywood Principle which says “Don’t call us, we’ll call you” with the use of Template Method Pattern, we are telling subclasses, “don’t call us, we’ll call you”.

Template pattern is good when we have a use case where the structure of an algorithm is the same and we provide different implementations, we can use the template method design pattern and also its a good fit for creating frameworks.

Lets take an example in one of our project we had written an api for handling breach journey. Once the system finds out that there is a breach in elements the system will use our breach journey api to validate and pass the breach to the required downstream interfaces. We had two types of breaches DynamicBreach and a StaticBreach and the flow of the algorithm was identical with few changes specific to type of breach. Here we will use the template design pattern to define the flow of our application and we will have subclasses redefine certain steps of an algorithm without changing the algorithms structure.

Below is the class structure we will be building for this example

Lets code the example

Lets start with our abstract BreachJourney which has the template method with the breach flow


public abstract class BreachJourney {

public void breachJourney(Breach breach) {
EnrichElementData enrichElementData = getEnrichData(breach.getElementId());
boolean duplicate = isDuplicate(breach.getElementId());
boolean upgrade = isCardInUpgradeList(breach.getElementId());

// we are providing a hook here which can be overriden by the base classes

if (isMessageRequired()) {
setMessageToBreach(breach);
}

if (!duplicate && !upgrade) {
publish(breach, enrichElementData);

}

}

public EnrichElementData getEnrichData(int id) {
return new EnrichElementData();
}

public boolean isDuplicate(int id) {
return false;
}

public abstract boolean isCardInUpgradeList(int id);

public abstract void publish(Breach breach, EnrichElementData enrichElementData);

public abstract Breach setMessageToBreach(Breach breach);

public boolean isMessageRequired() {
return false;
}

}

Lets code the concrete subclasses DynamicBreachJourney and StaticBreachJourney which has its own implementation for the methods isCardInUpgradeList,publish,setMessageToBreach and isMessageRequired


public class DynamicBreachJourney extends BreachJourney {

@Override
public boolean isCardInUpgradeList(int id) {
//logic specific to DynamicBreachJourney to determine whether the card is being upgraded
return false;
}

@Override
public void publish(Breach breach, EnrichElementData enrichElementData) {
//logic specific to DynamicBreachJourney to publish the breach into the interested subscribers.

}

@Override
public Breach setMessageToBreach(Breach breach) {
breach.setMessage("Breach in element id" + breach.getElementId() + "for metric id "
+ breach.getMetricMetadataId() + "for duration type " + breach.getDurationType());
return breach;
}

@Override
public boolean isMessageRequired()
{
return true;
}

}


public class StaticBreachJourney extends BreachJourney {

@Override
public boolean isCardInUpgradeList(int id) {
// logic specific to StaticBreachJourney to determine whether the card is being upgraded
return false;
}

@Override
public void publish(Breach breach, EnrichElementData enrichElementData) {
//logic specific to StaticBreachJourney to publish the breach into the interested subscribers.

}

@Override
public Breach setMessageToBreach(Breach breach) {
breach.setMessage(
"Breach in element id" + breach.getElementId() + "for metric id " + breach.getMetricMetadataId());
return breach;
}

@Override
public boolean isMessageRequired()
{
return true;
}

}

Lets code the domain objects used


public class Breach {

private int elementId;
private int metricMetadataId;
private int metricMetadataHourlyId;
private String durationType;
private int durationInterval;
private String message;

public Breach(int elementId, int metricMetadataId, int metricMetadataHourlyId, String durationType,
int durationInterval,String message) {
super();
this.elementId = elementId;
this.metricMetadataId = metricMetadataId;
this.metricMetadataHourlyId = metricMetadataHourlyId;
this.durationType = durationType;
this.durationInterval = durationInterval;
this.message=message;
}

public int getElementId() {
return elementId;
}

public void setElementId(int elementId) {
this.elementId = elementId;
}

public int getMetricMetadataId() {
return metricMetadataId;
}

public void setMetricMetadataId(int metricMetadataId) {
this.metricMetadataId = metricMetadataId;
}

public int getMetricMetadataHourlyId() {
return metricMetadataHourlyId;
}

public void setMetricMetadataHourlyId(int metricMetadataHourlyId) {
this.metricMetadataHourlyId = metricMetadataHourlyId;
}

public String getDurationType() {
return durationType;
}

public void setDurationType(String durationType) {
this.durationType = durationType;
}

public int getDurationInterval() {
return durationInterval;
}

public void setDurationInterval(int durationInterval) {
this.durationInterval = durationInterval;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

}


public class EnrichElementData {

private String subelement_name;
private String element_name;
private String element_vendor;

public EnrichElementData(int element_id, String subelement_name, String element_name, String element_vendor) {
super();
this.subelement_name = subelement_name;
this.element_name = element_name;
this.element_vendor = element_vendor;

}

public EnrichElementData() {
super();
// TODO Auto-generated constructor stub
}

public String getSubelement_name() {
return subelement_name;
}

public void setSubelement_name(String subelement_name) {
this.subelement_name = subelement_name;
}

public String getElement_name() {
return element_name;
}

public void setElement_name(String element_name) {
this.element_name = element_name;
}

public String getElement_vendor() {
return element_vendor;
}

public void setElement_vendor(String element_vendor) {
this.element_vendor = element_vendor;
}

public EnrichElementData getEnrichData(int element_id)
{
//Logic to derive the element properties required from element_id

return new EnrichElementData(100, "subelement_name", "element_name", "element_vendor");
}

}

Lets code the test class


public class Test {

public static void main(String[] args) {

Breach dynamicOne = new Breach(1, 2, 3, "SW", 5, "NA");

Breach staticOne = new Breach(8, 9, 4, "PW", 1, "NA");

BreachJourney dynamicBreach = new DynamicBreachJourney();
BreachJourney staticBreach = new StaticBreachJourney();

dynamicBreach.breachJourney(dynamicOne);

staticBreach.breachJourney(staticOne);

}

}