앞에 글에서 전략 패턴으로 UserDao를 변경해 봤다.  전략패턴의 기본구조에 익명 내부 클래스를 활용한 방식이었다. 이런방식을 스프링에서는 템플릿/콜백 패턴이라고 부른다. 전략 패턴의 컨텍스트를 템틀릿, 전략을 콜백이라고 부른다. 템를릿은 고정된 작업 흐름을 가진 코드를 재사용한다는 의미에서 붙여진 이름이다. 콜백은 템플릿 안에서 호출되는 것을 목적으로 만들어진 오브젝트이다. 

템플릿
템플릿(template)은 어떤 목적을 위해 미리 만들어둔 모양이 있는 틀을 가리킨다. 학생들이 도형을 그릴때 사용하는 도형자 또는 모양자가 바로 템플릿이다. 프로그래밍에서는 고정된 틀 안에 바꿀수 있는 부분을 넣어서 사용하는 경우에 템플릿이라고 부른다. JSP는 HTML이라는 고정된 부분에 EL과 스크립릿이라는 변하는 부분을 넣은 일종의 템플릿 파일이다. 템플릿 메소드 패턴은 고정된 틀의 로직을 가진 템플릿 메소드를 슈퍼클래스에 두고, 바뀌는 부분을 서브클래스의 메소드에 두는 구조로 이루어진다. 

콜백
콜백(callback)은 실행되는 것을 목적으로 다른 오브젝트의 메소드에 전달되는 오브젝트를 말한다. 파라미터로 전달되지만 값을 참조하기 위한 것이 아니라 특정 로직을 담은 메소드를 실행 시키키 위해 사용한다. 자바에선 메소드 자체를 파라미터로 전달할 방법은 없기 때문에 메소드가 담긴 오브젝트를 전달해야 한다. 그래서 Functional Object라고 한다. 

템플릿/콜백의 동작원리

1.템플릿/콜백 특징


  • 단일 메소드 인터페이스를 사용
  • 콜백은 하나의 메소드를 가진 인퍼페이스를 구현한 익명 내부 클래스 만들어짐
  • 콜백 인터페이스의 메소드는 보통 파라미터가 있음

여러개의 메소드를 가진 인터페이스를 사용 사용할 수 있는 전랙패턴의 전략과 달리 템플릿/콜백 패턴의 콜백은 보통 단일 메소드 인터페이스를 사용한다. 템플릿의 작업 흐름 중 특정 기능을 위해 한번 호출되는 경우가 일반적이기 때문이다.콜백은 일반적으로 하나의 메소드를 가진 인퍼페이스를 구현한 익명 내부 클래스로 만들어진다. 콜백 인터페이스의 메소드는 보통 파라미터가 있다. 템플릿의 작업 흐름중에 만들어지는 컨테스트 정보를 전달 받을때 사용된다. ( UserDao를 전략 패턴으로 변경 했을때 전략클래스의 메소드를 작성시  Context로 부터 connection을 파라미터로 전달 받았었다. )템플릿/콜백 방식은 전략 패턴과 DI장점을 익명 내부 클래스 사용 전략과 결합한 독특한 확용법이다. 

아래 그림은 템플릿/콜백의 작업흐름을 보여준다.  

2. 콜백의 분리와 재활용 

이번에는 복잡한 익명 내부클래스 사용을 최소화 할 수 있는 방법을 찾아보자. 

위 그림에서 처럼 변하는 부분은 변하지 않는 부분에서 분리 시켜 보자. 아래 반영한 코드를 작성해 두었다. 

//반복되는 부분 분리 
public void executeSQL(final String strSQL) throws SQLException{
	this.jdbcContext.workWithStatementStrategy(
		new StatementStrategy() {
			public PreparedStatement makeStatement(Connection c) throws SQLException {
				PreparedStatement ps=c.prepareStatement(strSQL);
				return ps;
			}
		}				
	);	
}
public void deleteAll() throws SQLException {		
	executeSQL("delete from users");
}	

그런데 executeSQL() 메소드는 다른 DAO에서도 사용할수 있으므로 템플릿 클래스 안으로 옮겨보자. 

package springbook.user.dao;

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

import javax.sql.DataSource;

public class JdbcContext {
	private DataSource dataSource;
	
	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	public void workWithStatementStrategy(StatementStrategy strategy) throws SQLException{
		Connection c = null;
		PreparedStatement ps = null;
		try {
			c = dataSource.getConnection();	
			ps=strategy.makeStatement(c); // <-- 이부분이 바뀌는 부분이다.. "전략" 적용하는 부분 
			ps.executeUpdate();
			
		}catch(SQLException e) {
			throw e;
		}finally {
			if(ps != null) {
				try {
					ps.close();
				}catch(SQLException e) {
					
				}
			}
			if(c != null) {
				try {
					c.close();
				}catch(SQLException e) {
					
				}
			}
		}
	}	

	//반복되는 부분 분리 
	public void executeSQL(final String strSQL) throws SQLException{
		workWithStatementStrategy(
			new StatementStrategy() {
				public PreparedStatement makeStatement(Connection c) throws SQLException {
					PreparedStatement ps=c.prepareStatement(strSQL);
					return ps;
				}
			}				
		);	
	}
}

 deleteAll()함수도 수정했다.

public void deleteAll() throws SQLException {		
		this.jdbcContext.executeSQL("delete from users");
}

콜백 재활용을 적용한 JdbcContext를 아래 그림으로 나타낼수 있다. 

이번에는 좀더 복잡한 add()메소드에도 적용해보자. add()메소드에는 PreparedStatement에 바인딩될 파라미터 내용이 추가 되어야 한다. 그래서 자바5에서 부터 제공하는 가변인자를 활용했다. 

public void add(final User user) throws SQLException {	
	this.jdbcContext.executeSQL("insert into users(id, name, password) values(?,?,?)",user.getId(),user.getName(),user.getName());
}

아래는 JdbcContext의 executeSQL에 가변인자를 추가한 코드다. 

public void executeSQL(final String strSQL, final String ...args) throws SQLException{		
	workWithStatementStrategy(
		new StatementStrategy() {
			public PreparedStatement makeStatement(Connection c) throws SQLException {
				int i=1;
				PreparedStatement ps=c.prepareStatement(strSQL);
				for(String a: args) {
					ps.setString(i++, a);
				}
				return ps;
			}
		}				
	);	
}

+ Recent posts