Nov 14, 2012

Template Method Design Pattern

Template Method Pattern
Template method pattern defines the skeleton of an algorithm in a method referred to as the Template Method deferring some steps to subclasses. The pattern is mostly used for building application frameworks where the framework implement the invariant pieces of the application's architecture and provide place holders or hooks for client customizations.

Examples from Java API
1. The Comparable interface defines the compareTo method which is a template method
2. The java applet class provides init, start, stop, paint and destroy hook methods for creating an applet
3. The actionPerformed method in ActionListener interface is a template method
4. The doGet and doPost methods in abstract class HttpServlet are examples of template methods


import java.awt.Color;
import java.awt.Graphics;

import javax.swing.JApplet;

/**
 * 
 * HelloWorldApplet.java
 */
public class HelloWorldApplet extends JApplet
{
    private String welcomeString = null;

    public void init() {
        welcomeString = "Hello World";
    }

    public void paint(Graphics g) {
        int x = (getParent().getWidth() / 2) - 50;
        int y = (getParent().getHeight()/ 2) - 50;
        g.drawString(welcomeString, x, y);
    }

    public void start() {}

    public void stop() {}

    public void destroy() {}
}

There are four types of methods which an abstract class may implement
1. Template. A template method is the one in abstract class that combines concrete, abstract and hook methods together into an algorithm. This method is final and not supposed to change.
2. Concrete. A concrete method is one defined in the abstract class and not implemented by subclasses.
3. Abstract. An abstract method is one declared in the abstract class and implemented by subclasses.
4. Hook. A hook method is declared and defined in the abstract class. Subclasses may implement this method to override the default behaviour.

The below example demonstrates a database transaction abstract class DBTx which executes all the queries specified for the transaction or none. The final public void execute() method is the template method which encompasses the algorithm used for executing the transaction. The method getQueryList is declared as abstract for subclasses to implement and return behavior specific queries.

package patterns.example.template;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

/**
 * 
 * File: DBTx.java
 */
public abstract class DBTx
{
    private Connection connection = null;

    protected abstract List<String> getQueryList();

    public DBTx(Connection connection) throws SQLException {
        this.connection = connection;
    }

    // template method
    final public void execute() throws SQLException {
        beginTxn();
        executeQuery();
        commit();
        endTxn();
    }

    private void beginTxn() throws SQLException {
        if (connection != null) connection.setAutoCommit(false);
    }

    private void executeQuery() {
        for (String query : getQueryList()) {
            executeQuery(query);
        }
    }

    private void executeQuery(String query) {
        Statement stmt = null;
        try {
            stmt = connection.createStatement();
            stmt.executeUpdate(query);
        }
        catch (SQLException e) {
            System.out.println("Failed to execute query " + query + " Error: " + e.getMessage());
            rollback();
        }
        finally {
            close(stmt);
        }
    }

    private void commit() throws SQLException {
        if (connection != null) connection.commit();
    }

    private void endTxn() throws SQLException {
        if (connection != null) connection.setAutoCommit(true);
    }

    private void rollback() {
        try {
            if (connection != null) connection.rollback();
        }
        catch (SQLException e) {
            System.out.println("Transaction rollback failed " + e.getMessage());
        }
    }

    private void close(Statement stmt) {
        try {
            if (stmt != null) {
                stmt.close();
            }
        }
        catch (SQLException e) {
            // ignore
        }
    }
}

package patterns.example.template;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * 
 * File: BalanceUpdater.java
 */
public class BalanceUpdater extends DBTx
{
    public BalanceUpdater(Connection connection) throws SQLException {
        super(connection);
    }

    @Override
    protected List<String> getQueryList() {
        List<String> queryList = new ArrayList<String>();
        queryList.add("UPDATE sample.employee SET balance = balance + 10 WHERE emp_id = 1");
        queryList.add("UPDATE sample.employee SET balance = balance + 11 WHERE emp_id = 4");
        return queryList;
    }
}

package patterns.example.template;

/**
 * 
 * File: DBConfig.java
 */
public interface DBConfig
{
    String getDriverName();

    String getConnectionString();

    String getUserName();

    String getPassword();
}

package patterns.example.template;

/**
 * 
 * File: MySQLConfig.java
 */
public class MySQLConfig implements DBConfig
{
    private static final String DB_CONNECTION_STR = "jdbc:mysql://localhost:3306/sample";
    private static final String USERNAME = "pankaj";
    private static final String PASSWORD = "pankaj";
    private static final String DRIVER_NAME = "com.mysql.jdbc.Driver";

    @Override
    public String getDriverName() {
        return DRIVER_NAME;
    }

    @Override
    public String getConnectionString() {
        return DB_CONNECTION_STR;
    }

    @Override
    public String getUserName() {
        return USERNAME;
    }

    @Override
    public String getPassword() {
        return PASSWORD;
    }
}

The ConnectionManager class for managing jdbc connections
package patterns.example.template;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * 
 * File: ConnectionManager.java
 */
public class ConnectionManager
{
    private DBConfig config = null;

    public ConnectionManager(DBConfig config) {
        this.config = config;
    }

    public Connection getConnection() {
        try {
            Class.forName(config.getDriverName());
            return DriverManager.getConnection(config.getConnectionString(), config.getUserName(), config.getPassword());
        }
        catch (ClassNotFoundException e) {
            System.out.println("Unable to register driver for the database " + e.getMessage());
        }
        catch (SQLException e) {
            System.out.println("Unable to get connection for the database " + e.getMessage());
        }
        return null;
    }

    public void closeConnection(Connection connection) {
        try {
            connection.close();
        }
        catch (SQLException e) {
            // ignore
        }
    }
}

The client program
package patterns.example.template;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * 
 * File: TemplateExample.java
 */
public class TemplateExample
{
    private static ConnectionManager conxnMgr = null;
    private static Connection conxn = null;

    public static void main(String[] args) {
        init();
        try {
            DBTx txn = new BalanceUpdater(conxn);
            txn.execute();
        }
        catch (SQLException e) {
            System.out.println("Failed to update balance " + e.getMessage());
        }
        clean();
    }

    private static void init() {
        conxnMgr = new ConnectionManager(new MySQLConfig());
        conxn = conxnMgr.getConnection();
    }

    private static void clean() {
        conxnMgr.closeConnection(conxn);
    }
}

Class Diagram

References:
Avajava
tech.puredanger.com

No comments :

Post a Comment