Template Method Design Pattern

0

Template Method design pattern defines the steps of an algorithm and the order in which they should be performed. We need this when we have multiple forms of the same algorithm with minor variations implemented in different places.

In software DRY (don’t repeat yourself) is an important concept. Repetition can be of code, data or a pattern. In this case, it is of a pattern which we eliminate by abstracting out the commonalities into a template that goes into a abstract super-class and let the sub-classes extend the template and customize it their way.

Note the  abstract super-class only creates a template and doesn’t how to implement all the steps.

For example, consider an issue tracking system where a bug goes through a cycle of state changes. For example, when a qa creates a bug, it first gets assigned to a module leader. Module leader reviews it and assigns it to a developer. Developer imparts the fix, resolves the issue. Issue gets assigned to a QA. QA tests the bug and either closes it or reopens it.

We can easily identify the various tasks involved in an issue tracking system like ‘Create New Issue’, ‘Assign Issue’, ‘Resolve Issue’, ‘Close Issue’/’Reopen Issue’.

 

issue_tracking_template

Multiple forms of template

 

 

Each task involves some validations, state changes in the bug and notifications that are sent to the parties interested in keeping a watch on the issue.

 

task_algorithm

Template pattern algorithm

 

Since all the tasks follow similar algorithm, instead of letting the sub-classes manage the flow, the control is inverted so that the flow is driven by abstract super-classes. The subclass’s concrete methods chip in whenever called in the flow of the algorithm.

 

IssueTrackingTemplateClassDiagram

Template Pattern Example Classdiagram

 

 

You can notice that AbstractTask handles the main algorithm which is called through method execute().
It is better to define the types in interfaces rather than than classes.
In our example, the execute() method is in the interface Task and the abstract super-class AbstractTask implements Task. It implements execute but the individual steps are protected methods in AbstractTask.

package javarticles.templatepattern;

public interface Task {
    void execute();

}

Always make sure abstract parent class implements an interface. You can see below AbstractTask implements Task.

package javarticles.templatepattern;

public abstract class AbstractTask implements Task {
    @Override
    public final void execute() {
        validateTask();
        updateTaskState();
        notifyInterestedParties();        
    }
    
    protected abstract void validateTask();
    
    protected abstract void updateTaskState();
    
    protected abstract void notifyInterestedParties();
}

How should we design the abstract super-class?

Idea is to encapsulate the individual steps into a final method and provide abstract methods for steps which the abstract class doesn’t know how to implement and let concrete classes of this abstract super-class implement it. The workflow is controlled by the abstract super-class as it knows exactly the steps and the order in which they have to be performed. The methods to be implemented are abstract and protected so that the concrete classes are forced to override the methods.

I have provided here few sub-classes. You can download the entire code here templatepattern.zip.

NewBugTask

package javarticles.templatepattern;

public class NewBugTask extends AbstractTask {
    private Issue issue;
    
    public NewBugTask(Issue issue) {
        this.issue = issue;        
    }

    @Override
    protected void validateTask() {
        System.out.println("NewBug: validate if a module leader exists for module " + issue.getModuleLeader().getName());
        TeamMember moduleLeader = issue.getModuleLeader();
        if (moduleLeader == null) {
            throw new RuntimeException("No moduler leader assigned for module <");
        }
    }

    @Override
    protected void updateTaskState() {
        System.out.println("NewBug: Create new issue and assign to module leader " + issue.getModuleLeader().getName());
        this.issue.setStatus(StatusEnum.NEW);
        TeamMember moduleLeader = issue.getModuleLeader();
        moduleLeader.addToRoadmap(issue);
        issue.setAssignedTo(moduleLeader);
    }

    @Override
    protected void notifyInterestedParties() {
        System.out.println("NewBug: notify interested parties");
        issue.getAssignedTo().notifyMember();
        issue.getProject().notifyProjectLead();
        issue.getReportedBy().notifyMember();
        issue.getProject().notifyQaLead();
    }

}

 

ResolveBugTask

package javarticles.templatepattern;

public class ResolveBugTask extends AbstractTask {
    private Issue issue;

    public ResolveBugTask(Issue issue) {
        this.issue = issue;
        this.issue.setStatus(StatusEnum.RESOLVED);
    }
    
    @Override
    protected void validateTask() {
        System.out.println("ResolveBug: validate if the code is reviewed");
        if (issue.getIsCodeReviewRqd() && issue.getIsCodeReviewDone()) {
            throw new RuntimeException("Code review required");
        }
    }

    @Override
    protected void updateTaskState() {
        System.out.println("ResolveBug: resolve issue");
        issue.getDev().removeFromRoadmap(issue);
        System.out.println("ResolveBug: issue resolved, removed from dev " + issue.getDev().getName() + " roadmap");
        TeamMember qa = issue.getQa();
        qa.addToRoadmap(issue);
        issue.setAssignedTo(qa);        
        System.out.println("ResolveBug: assigned to qa " + issue.getQa().getName());
    }

    @Override
    protected void notifyInterestedParties() {
        System.out.println("ResolveBug: notify interested parties");
        issue.getDev().notifyMember();
        issue.getQa().notifyMember();
        issue.notifyInterestedParties();   
    }
}

Run the example of Template Design Pattern

    @Test
    public void issueFlow() {
        Project project = new Project();
        Issue issue = project.createIssue("Example of template pattern", "Tom", "ModuleA");
        issue.assignTo("Ram");
        issue.resolve();
        issue.close("Qa done");
    }

Output

NewBug: validate if a module leader exists for module ModuleA
NewBug: Create new issue and assign to module leader ModuleA
NewBug: notify interested parties
notify ModuleA
notify project lead
notify Tom
notify qa lead
AssignBug: validate whether dev Ram exists
AssignBug: assign the bug to Ram
AssignBug: notify interested parties
notify Ram
notify Tom
notify others if any
ResolveBug: validate if the code is reviewed
ResolveBug: resolve issue
ResolveBug: issue resolved, removed from dev Ram roadmap
ResolveBug: assigned to qa Tom
ResolveBug: notify interested parties
notify Ram
notify Tom
notify others if any
CloseBug: validate if qa notes exists
CloseBug: close the issue
CloseBug: remove from qa Tom roadmap
CloseBug: notify interested parties
notify Ram
notify Tom
notify others if any

Important Points on Template Design Pattern

  • Use Template Method Pattern to capture an algorithm in an abstract super-class but defer implementations of individual steps to sub-classes.
  • Abstract super-class should take the burden of writing tricky part. The methods that may change between sub-classes must be made abstract.
  • Abstract super-classes are also often used to implement some, but not all, methods of an interface.
  • The remaining methods which vary between concrete implementations are left unimplemented.
  • This differs from Template Method Pattern in that abstract super-class doesn’t handle workflow.

Usages of Template Design Pattern

  • All non-abstract methods of InputStream, OutputStream, Reader and Writer.
  • All non-abstract methods of AbstractList, AbstractSet and AbstractMap.
  • In servlet API, class HttpServlet implements template pattern, its methods like doGet(), doPost(), doPut() has the main algorithm. Sub-classes will tweak the behavior by overriding the abstract methods.
  • In spring, classes like JdbcTemplate and JmsTemplate classes has methods like update(), query(), execute() etc. It encapsulates the common code like getting a Connection, creating a Session, closing the connection. Actual action is called through a callback method.

Download source code

In this article, I have shown you an example of Template Method Pattern.
You can download the entire code here templatepattern.zip

About Author

Ram’s expertise lies in test driven development and re-factoring. He is passionate about open source technologies and loves blogging on various java and open-source technologies like spring.
You can reach him at [email protected]

Leave A Reply