상세 컨텐츠

본문 제목

[3.2] 변하는 것과 변하지 않는 것

토비의스프링

by kwanghyup 2020. 10. 28. 20:01

본문

분리와 재사용을 위한 디자인 패턴 적용

 

deleteAll()코드를 다시 살펴보자. 

public void deleteAll() throws SQLException {
	
	Connection c = null;
	PreparedStatement ps = null;
	
	try {  
		c = dataSource.getConnection();
		
		String sql = "truncate users";
		ps = c.prepareStatement(sql); // 변하는 부분 
		
		ps.executeUpdate();
	} catch (SQLException e) {
		throw e; 
	} finally {
		
		if(ps!=null) {
			try {
				ps.close();
			} catch (SQLException e) {
				// ps.close() 메서드에서도 SQLException 예외가 발생할 수 있다.  
			}
		}
		
		if(c!=null) {
			try {
				c.close();
			} catch (SQLException e) {
				// ps.close() 메서드에서도 SQLException 예외가 발생할 수 있다.
			}
		}
	}		
}

 

PreparedStatement를 만들어 업데이트용 쿼리를 실행하는 메소드라면

deleteAll()메소드와 비슷한 구조를 가진다.

 

다음의 코드를 제외한 나머지부분은 비슷한 기능을 가진 메소드에서 동일하게 나타난다. 

String sql = "truncate users";
ps = c.prepareStatement(sql);

 

메소드 추출

 

변하는 부분을 메소드로 추출하여 보자. 

public void deleteAll() throws SQLException {
	
	Connection c = null;
	PreparedStatement ps = null;
	
	try {  
		c = dataSource.getConnection();
		
		ps = makeStatement(c); // 변하는 부분에서 메소드 호출 
		
		ps.executeUpdate();
	} catch (SQLException e) {
		throw e; 
	} finally {
		// 생략...
	}		
}

private PreparedStatement makeStatement(Connection c) throws SQLException {
	return c.prepareStatement("truncate users");  
}

분리시킨 메소드를 다른 곳에서 재사용할 수 있어야 하는데

분리시키고 남은 메소드가 재사용이 필요한 부분이다.

반대로 된 상황이다.

 

전략 패턴의 적용

 

StatementStrategy인터페이스

package springbook.user.dao;

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

public interface StatementStrategy {
	PreparedStatement makePreparedStatement(Connection c) throws SQLException;
}

 

이 인터페이스를 상속해 실제 전략

즉 바뀌는 부분인 PreparedStatment를 생성하는

클래스를 만들어보자.

 

deleteAll()메소드의 기능을 구현한 StatementStrategy 전략 클래스

package springbook.user.dao;

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

public class DeleteAllStatement implements StatementStrategy{

	@Override
	public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
		return c.prepareStatement("truncate users");
	}

}

 

 UserDao의 DeleteAll()메소드에서 사용한다.

public void deleteAll() throws SQLException {
	
	Connection c = null;
	PreparedStatement ps = null;
	
	try {  
		c = dataSource.getConnection();
		
        // 수정된 부분
		StatementStrategy strategy = new DeleteAllStatement();
		strategy.makePreparedStatement(c);
		
		ps.executeUpdate();
	} catch (SQLException e) {
		throw e; 
	} finally {
		
		if(ps!=null) {
			try {
				ps.close();
			} catch (SQLException e) {
				// ps.close() 메서드에서도 SQLException 예외가 발생할 수 있다.  
			}
		}
		
		if(c!=null) {
			try {
				c.close();
			} catch (SQLException e) {
				// ps.close() 메서드에서도 SQLException 예외가 발생할 수 있다.
			}
		}
	}		
}

 

컨텍스트가 StatementStrategy인터페이스 뿐만 아니라

특정 구현 클래스인 DeleteAllStatement를 

직접 알고 있다는 것은 전략패턴에도

OCP에도 잘 들어 맞지 않는다.

 

# DI적용을 위한 클라이언트/컨텍스트 분리 

 

try/catch/finally코드를 StatementStrategy를

만드는 부분에서 독립시켜야한다.

 

public void jdbcContextWithStatementStrategy(StatementStrategy stmt) throws SQLException {
	Connection c = null;
	PreparedStatement ps = null;
	
	try {
		c = dataSource.getConnection();
		ps = stmt.makePreparedStatement(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){}}
	}
			
}

 

클라이언트를 담당할 deleteAll메소드

public void deleteAll() throws SQLException { 
		jdbcContextWithStatementStrategy(new DeleteAllStatement());
}

 

 

관련글 더보기

댓글 영역