Monday, October 4, 2010

Design Patterns in Java - Template Method

Introduction

This is first article about OO design patterns, we will start with the most useful pattern.
Template method (GoF - Gang Of Four) present good example of using concrete inheritance. If you ask me, using inheritance in this way is far more productive than using concrete inheritance for code reuse. Everything you think you can do with concrete inheritance, you can do also with plain object composition. But, ok...never mind about that, let's write something about template method pattern.

You can download example of this Java code from my repository on google code:
https://code.google.com/p/codingwithpassionblog/source/browse/trunk/src/org/codingwithpassion/patterns/templateMethod/DecorateData.java

Usage

This pattern address common problem: we know the steps of an algorithm and the order in which they should be performed, but we don't know (or don't care) how to perform all of the steps. Template method encapsulate individual steps we don't know how to perform to abstract methods in abstract superclass. Concrete subclass will implement these methods. These method represent individual steps in an algorithm. In this way, superclass (abstract one) will control the flow (workflow) of algorithm. Subclasses just fill the gaps in superclass algorithm.

In this example this centralized workflow logic is example of inversion of control. In this approach the superclass is calling methods from subclass. This is fundamental approach for lean and clean code and code reuse.

Let's see example (this example can be put in one file for easy testing - it have only one public class):
/**
 * Abstract implementation -- class that control flow.
 * Describe algorithm for generic object creation.
 *
 */
abstract class CreateObject {
 
 protected Object[] datas = {"Kimmi", "Zuco", 1, 21};
 
 public void setData(Object[] data) {
  CreateObject.this.datas = data;
 }
 
 /**
  * Algorithm that control flow (IOC - Inversion of Control).
  * @return Object String representation. 
  */
 public String decorate() {
  StringBuilder sb = new StringBuilder();
  objectStart(sb);
  
  for (int i = 0; i < datas.length; i++) {
   Object data = datas[i];
   if (data instanceof String) {
    stringValue(sb, data, i);
   } else if (data instanceof Integer) {
    numberValue(sb, data, i);
   }
  }
  objectEnd(sb);  
  return sb.toString();
 }
 
 //these classes (down) need to be implemented in subclasses.
 abstract void objectStart(StringBuilder sb);
 
 abstract void objectEnd(StringBuilder sb);
 
 abstract void stringValue(StringBuilder sb, Object value, int indx);
 
 abstract void numberValue(StringBuilder sb, Object value, int indx);
 
}

/**
 * Object creation for JSON objects;
 *
 */
class JSONObject extends CreateObject {
 
 protected void objectStart(StringBuilder sb) {
  sb.append("\"Object\":").append("\n{");
 }
 
 protected void objectEnd(StringBuilder sb) {
  sb.append("\n}");
 }
 
 protected void stringValue(StringBuilder sb, Object value, int indx) {
  sb.append("prop")
    .append("\"").append(indx).append("\":")
    .append("\"").append(value).append("\",")
    .append("\n");
    
 }
 
 protected void numberValue(StringBuilder sb, Object value, int indx) {
  sb.append("prop")
    .append("\"").append(indx).append("\":")
    .append(value).append(",")
    .append("\n");
 }
}

/**
 * Object creation for xml objects.
 *
 */
class XmlObject extends CreateObject {
 
 protected void objectStart(StringBuilder sb) {
  sb.append("").append("\n");  }    protected void objectEnd(StringBuilder sb) {   sb.append("");
 }
 
 protected void stringValue(StringBuilder sb, Object value, int indx) {
  sb.append("")
    .append("prop")
    .append(indx)
    .append("")
    .append(value)
    .append("")
    .append("")
    .append("\n");  
 }
 
 protected void numberValue(StringBuilder sb, Object value, int indx) {
  sb.append("")
    .append("prop")
    .append(indx)
    .append("")
    .append(value)
    .append("")
    .append("")
    .append("\n");  
 }
 
}

public class DecorateData {
 
 public static void main(String[] args) {
  CreateObject xml = new JSONObject();
  System.out.println(xml.decorate());
 }
 
}
Lot of stuff going of there, but don't worry. Main thing to understand is flowing:

6 - 32: We declare abstract class that encapsulate workflow of creating string representation of object.
18 - 32: Implementation of algorithm - workflow. This algorithm use abstract methods (35 - 42) that need to be implemented by subclasses (He calls these methods from subclasses, hence Inversion of Control paradigm).
49 - 73: First implementation that implement abstract method and create JSON string object representation...nothing fancy.
79 - 111: Same thing, but for Xml Object representation.

112 - 120: Testing...

Conclusion

Such patterns as this offer good paradigm for separation of concerns. We can for example create superclass that will concentrate on business logic (and flow of that business logic), and use subclasses for primitive operation (technical stuff, JPA, servlets, parsing, JDBC,...).

It is useful to use Template method patter to capture an algorithm in one place, but deffer implementation of simple steps to subclasses. This has the potential to avoid bugs, by getting tricky operations right once in superclass and simplifying user code.

Please check post about Strategy design pattern that is very similar like Template, but instead abstract class uses interface.

No comments:

Post a Comment