1. 程式人生 > >模板方法(Template Method)模式

模板方法(Template Method)模式

模板方法(Template Method)模式定義:

Define the skeleton of an algorithm in an operation , deferring some steps to subclasses . Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure .

定義一個操作中的一個演算法框架,把一些步驟推遲到子類去實現。模板方法模式讓子類不需要改變演算法結構而重新定義特定的演算法步驟。

也就是說:模板方法模式定義了一系列演算法步驟,子類可以去實現或覆蓋其中某些步驟,但不能改變這些步驟的執行順序。

下面的程式碼是模板方法模式的示例程式碼,類HappyPeople的方法celebrateSpringFestival()是一個模板方法,有3個步驟,其中方法travel()是抽象部分,用於子類實現不同客戶化邏輯。

package pattern.part1.chapter2.template;

/**
 * Date: 2009-11-18
 * Time: 0:42:25
 */
public class HappyPeopleTestDrive {
    public static void main(String[] args) {
        HappyPeople passengerByAir = new PassengerByAir();
        HappyPeople passengerByCoach = new PassengerByCoach();
        HappyPeople passengerByTrain = new PassengerByTrain();

        System.out.println("Let's Go Home For A Grand Family Reunion...\n");

        System.out.println("Tom is going home:");
        passengerByAir.celebrateSpringFestival();

        System.out.println("\nRoss is going home:");
        passengerByCoach.celebrateSpringFestival();

        System.out.println("\nCatherine is going home:");
        passengerByTrain.celebrateSpringFestival();
    }
}

package pattern.part1.chapter2.template;


public abstract class HappyPeople {
    public void celebrateSpringFestival() {
        subscribeTicket();
        travel();
        celebrate();
    }

    protected final void subscribeTicket() {
        //Buying ticket...
        System.out.println("Buying ticket...");
    }

    protected abstract void travel();

    protected final void celebrate() {
        //Celebrating Chinese New Year...
        System.out.println("Happy Chinese New Year!");
    }
}

package pattern.part1.chapter2.template;

/**
 * Date: 2009-11-18
 * Time: 0:23:43
 */
public class PassengerByAir extends HappyPeople {
    @Override
    protected void travel() {
        //Traveling by Air...
        System.out.println("Travelling by Air...");
    }
}

package pattern.part1.chapter2.template;

/**
 * Date: 2009-11-18
 * Time: 0:22:16
 */
public class PassengerByCoach extends HappyPeople {
    @Override
    protected void travel() {
        //Travel by Coach...
        System.out.println("Travelling by Coach...");
    }
}

package pattern.part1.chapter2.template;

/**
 * Date: 2009-11-18
 * Time: 0:20:05
 */
public class PassengerByTrain extends HappyPeople {
    @Override
    protected void travel() {
        //Travel by Train...
        System.out.println("Travelling by Train...");
    }
}

模版方法模式可以解決某些場景中的程式碼冗餘問題,但也可能引入類的泛濫問題。使用回撥可以避免類的泛濫。

回調錶示一段可執行邏輯的引用(或者指標),我們把該引用(或者指標)傳遞到另外一段邏輯(方法)裡供這段邏輯適時呼叫。

比如針對以下需求:查詢資料庫的記錄。

1.得到資料庫連線Connection物件;

2.建立Statement物件並執行相關的查詢語句;

3.處理查詢出來的結果,並在整個執行過程中處理異常。

整個過程中發生變化的主要是第3步,適合用模板模式來處理這個問題,但由於各種各樣的查詢太多,導致我們需要建立很多的子類來處理這些查詢結果,會引起子類的泛濫。因此,我們可以使用回撥(Callback)來解決這個問題。

package com.chapter2.callback;
import static org.junit.Assert.assertEquals;

import java.sql.ResultSet;

import org.junit.Test;
public class TemplateTestDrive {
	@Test
	public void testTemplate(){
		boolean called = new SimpleJDBCTemplate()
		.query("Select * from DB", 
				new ResultSetHandler<Boolean>(){
					@Override
					public Boolean handle(ResultSet rs) {
						return true;
					}
			});
		assertEquals(true,called);
	}

}

package com.chapter2.callback;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class SimpleJDBCTemplate {
	public <T> T query(String queryString,ResultSetHandler<T> rsHandler){
		Connection connection = null;
		PreparedStatement statement=null;
		ResultSet rs=null;
		try {
			connection = ConnectionUtils.getConnection();
			statement = connection.prepareStatement(queryString);
			rs = statement.executeQuery();
			return rsHandler.handle(rs);
		} catch (SQLException ex) {
			closeStatement(statement);
			statement=null;
			releaseConnection(connection);
			connection=null;
			throw new RuntimeException("a sql exception occured",ex);
		}finally{
			closeStatement(statement);
			releaseConnection(connection);
		}
		
	}
	
	private void releaseConnection(Connection conn){
		if(conn!=null){
			try {
				conn.close();//close the connection or put it back  to the connection pool
			} catch (SQLException e) {
				//todo handle SQLException
			} catch (Throwable ex) {
                //todo handle other exception
            }
		}
	}
	
	private void closeStatement(PreparedStatement statement){
		if(statement!=null){
			try {
				statement.close();
			} catch (SQLException e) {
				//todo handle SQLException
			} catch (Throwable ex) {
                //todo handle other exception
            }
		}
	}
}

package com.chapter2.callback;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class ConnectionUtils {
	public static Connection getConnection(){
		Connection connection = createMock(Connection.class);
		PreparedStatement statement = createMock(PreparedStatement.class);
		try {
			expect(connection.prepareStatement((String)anyObject())).andReturn(statement);
			expect(statement.executeQuery()).andReturn(null);
			replay(connection);
			replay(statement);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return connection;
	}

}
package com.chapter2.callback;

import java.sql.ResultSet;

public interface ResultSetHandler<T> {
	public T handle(ResultSet rs);
}

注:本文大部分內容點來自劉濟華的《漫談設計模式》