분리와 재사용을 위한 디자인 패턴 적용
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());
}
[3.4] 컨텍스트와 DI (0) | 2020.10.29 |
---|---|
[3.3] JDBC 전략 패턴의 최적화 (0) | 2020.10.28 |
[3.1] 다시 보는 초난감DAO (0) | 2020.10.28 |
[2.4] 스프링 테스트 적용 (0) | 2020.10.28 |
[2.3] 개발자를 위한 테스팅 프레임워크 Junit (0) | 2020.10.28 |
댓글 영역