Definition
The Template Method Pattern defines the steps of an algorithm and allow subclasses to provide the implementation for one or more steps.
Figure 01. Class diagram Template method pattern. Ref.:http://programmersnotes.info/2009/03/03/difference-between-adapter-and-template-method-pattern/
Scenario
A telecommunication provider wants to automate its order process. For now the telecommunication provider offers internet and telephony service and they may offer in the future cable, or any other type of services.
To present a solution to this requirement, it was noticed that the order generation for telephony and internet were almost similar with small differences, is for this reason that for the order generation, the Template method pattern fits properly and as a plus, this design pattern is very flexible to add in the future other kind of offers that the telco provider may want to add.
The class diagram to generate orders is as shown in the next picture:
Figure 02. Template pattern applied to the order generation.
Figure 03. Template pattern applied to a simple Order printer to test the implementation.
Talking about the first Template pattern example, the classes InternetOrderGenerator and TelephonyOrderGenerator do almost the same thing with small differences, is for this reason that generalizing the common steps in the abstract class OrderGenerator, and by this way applying the Template Method pattern, is the best way to go and as a plus this pattern is flexible to extending this order generation to other kind of services that may come in the future.
The code of the abstract class is as follows:
OrderGenerator.java
package chris.designpatterns.template; import java.util.List; import java.util.Random; import chris.designpatterns.template.model.Order; import chris.designpatterns.template.model.Product; public abstract class OrderGenerator { protected List<Product> products; public Order generate() { Order order = createOrder(); //First step: Add products from the basket setProducts(); //set order particular characteristics setCharacteristics(); //calculate price float total = priceCalculator(); order.setCost(total); //generate order id int orderId = generateId(); order.setId(orderId); return order; } public abstract Order createOrder(); public abstract void setCharacteristics(); public abstract void setProducts(); private int generateId() { return new Random().nextInt(100000000); } private float priceCalculator() { float total = 0; for(Product p : products){ total += p.getPrice(); } return total; } }
The subclasses are simpler because a big part of the logic is done in the template abstract class.
TelephonyOrderGenerator.java
package chris.designpatterns.template; import java.util.ArrayList; import chris.designpatterns.template.model.Order; import chris.designpatterns.template.model.Product; import chris.designpatterns.template.model.TelephoneProduct; import chris.designpatterns.template.model.TelephonyOrder; import chris.designpatterns.template.model.TelephonyPlanProduct; public class TelephonyOrderGenerator extends OrderGenerator { private TelephoneProduct telephone; private TelephonyPlanProduct plan; private TelephonyOrder order; public TelephonyOrderGenerator(TelephoneProduct telephone, TelephonyPlanProduct plan){ this.telephone = telephone; this.plan = plan; } @Override public void setCharacteristics() { TelephonyOrder telOrder = (TelephonyOrder) order; telOrder.setPhoneNumber(telephone.getNumber()); telOrder.setPlanMinutes(plan.getMinutes()); } @Override public Order createOrder() { this.order = new TelephonyOrder(); return order; } @Override public void setProducts() { products = new ArrayList<Product>(); products.add(telephone); products.add(plan); order.setProducts(products); } }
InternetOrderGenerator.java
package chris.designpatterns.template; import java.util.ArrayList; import chris.designpatterns.template.model.InternetOrder; import chris.designpatterns.template.model.InternetProduct; import chris.designpatterns.template.model.Order; import chris.designpatterns.template.model.Product; public class InternetOrderGenerator extends OrderGenerator { private InternetProduct internet; private InternetOrder order; public InternetOrderGenerator(InternetProduct internet) { this.internet = internet; } @Override public void setCharacteristics() { order.setInternetSpeed(internet.getSpeed()); order.setRouterBrand(internet.getRouterBrand()); } @Override public Order createOrder() { order = new InternetOrder(); return order; } @Override public void setProducts() { products = new ArrayList<Product>(); products.add(internet); order.setProducts(products); } }
Notice the way the abstract methods were implemented in both classes.
And the client that will test our implementation:
Main.java
package chris.designpatterns.template; import chris.designpatterns.template.model.InternetFactory; import chris.designpatterns.template.model.InternetProduct; import chris.designpatterns.template.model.Order; import chris.designpatterns.template.model.TelephoneFactory; import chris.designpatterns.template.model.TelephoneProduct; import chris.designpatterns.template.model.TelephonyPlanFactory; import chris.designpatterns.template.model.TelephonyPlanProduct; import chris.designpatterns.template.utility.OrderPrinter; import chris.designpatterns.template.utility.OrderPrinterInternet; import chris.designpatterns.template.utility.OrderPrinterTelephony; public class Main { public static void main(String[] args) { Main main = new Main(); //Telephony Order main.runTelephonyOrder(); //Internet Order main.runInternetOrder(); } private void runInternetOrder() { InternetProduct internet = InternetFactory.createProduct(); internet.setRouterBrand("Cisco 3500"); OrderGenerator orderGenerator = new InternetOrderGenerator(internet); Order order = orderGenerator.generate(); OrderPrinter printer = new OrderPrinterInternet(); printer.print(order); } private void runTelephonyOrder() { TelephoneProduct telephone = TelephoneFactory.createProduct(); telephone.setNumber("0652630233"); TelephonyPlanProduct telephonyPlan = TelephonyPlanFactory.createProduct(); OrderGenerator orderGenerator = new TelephonyOrderGenerator(telephone, telephonyPlan); Order order = orderGenerator.generate(); OrderPrinter printer = new OrderPrinterTelephony() printer.print(order); } }
Another example of the implementation of the Template method pattern can be checked in the classes:
- OrderPrinter
- OrderPrinterInternet
- OrderPrinterTelephony
The code of the full project is attached to this post.
Hope you find it useful and as always, if you have any questions and/or recommendations, feel free to make them.
