JDBC try/catch/finally 코드의 문제점
-복잡한 블록이 2중으로 중첩까지 되어 있어 조금이라도 코드에 실수가 있을 시 리소스가 부족한 심각한 문제가 발생할 가능성이 높다.
-풀이 가득차버려(리소스가 부족하여) 잘못된 부분을 찾아나가는 과정에서도 코드의 양의 광범위해 찾기가 쉽지않다.
이러한 문제점들 해결하려면?
많은 곳에서 중복되지만 변하지 않는 코드와 로직에 따라 자꾸 확장되고 자주 변하는 코드를 분리해내는 작업 필요
분리와 재사용을 위한 디자인 패턴 적용
-메소드 추출 리팩토링
변하는 부분을 메소드로 추출
public void deleteAll() throws SQLException {
Connection c = null;
PreparedStatement ps = null;
try {
c = dataSource.getConnection();
ps = makeStatement(c);
ps.execute();
} catch (Exception e) {
throw e;
}finally {
if(ps != null) { try { ps.close(); } catch (SQLException e) {} }
if (c != null) { try { c.close(); } catch (SQLException e) {} }
}
}
private PreparedStatement makeStatement(Connection c) throws SQLException {
PreparedStatement ps;
ps = c.prepareStatement("delete from users");
return ps;
}
문제점
보통 분리시킨 메소드를 다른 곳에서 재사용하는데 위와 같은 방식은 재사용이 불가능하다.
변하지 않는 부분을 메소드로 추출하는 것은 try/catch/finally 구조상 쉽지않은 상황
-템플릿 메소드 패턴의 적용
템플릿 메소드 패턴
:상속을 통해 기능을 확장해서 사용
변하지 않는 부분은 슈퍼클래스에 변하는 부분은 추상 메소드로 정의 후 서브클래스에서 오버라이드하여 정의 후 사용
변하지 않는 try/catch/finally 블록 슈퍼클래스와 상속을 통해 바꿀 수 있는 부분은(위의 예시 PreparedStatement 부분) 서브클래스로 분리
문제점
-다중 상속이 되지 않기 때문에 DAO 로직마다 상속을 통해 새로운 클래스를 만들어야 한다.
-확장구조가 이미 클래스를 설계 시점에서 고정된다.
-전략패턴의 적용
오브젝트를 아예 둘로 분리하고 클래스 레벨에서는 인퍼페이스를 통해서만 의존하도록 만드는 패턴
개방 폐쇄 원칙에 잘 부합하고 유연하면서 확장성이 좋다.
확장이 되는 부분이 변화되는 부분으로 별도의 클래스로 만들어 추상화된 인터페이스를 통해 위임
public interface StatementStrategy {
PreparedStatement makePreparedStatement(Connection c) throws SQLException;
}
public class DeleteAllStatement implements StatementStrategy {
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
PreparedStatement ps = c.prepareStatement("delete from users");
return ps;
}
}
public void deleteAll() throws SQLException {
Connection c = null;
PreparedStatement ps = null;
try {
c = dataSource.getConnection();
StatementStrategy strategy = new DeleteAllStatement();
ps = strategy.makePreparedStatement(c);
ps.execute();
} catch (Exception e) {
throw e;
}finally {
if(ps != null) { try { ps.close(); } catch (SQLException e) {} }
if (c != null) { try { c.close(); } catch (SQLException e) {} }
}
}
문제점
StatementStrategy strategy = new DeleteAllStatement(); 코드를 보면
인터페이스뿐만 아니라 특정 클래스까지 알고있으므로 부적합
-DI 적용을 위한 클라이언트/컨텍스트 분리
어떤 전략을 사용할지 클라이언트에게 결정권을 준다.
클라이언트가 전략 하나를 선택하고 오브젝트로 만들어서 컨텍스트에 전달
컨텍스트는 전달받은 전략 구현 클래스의 오브젝트 사용
클라이언트 역할을 할 deleteAll 메소드 예시 코드
public void deleteAll() throws SQLException {
StatementStrategy st = new DeleteAllStatement();
jdbcContextWithStatementStrategy(st);
}
DI의 중요한 개념은 제3자의 도움을 통해 두 오즈젝트 사이의 유연한 관계가 설정되는 것!
'Spring > 토비의 스프링' 카테고리의 다른 글
[토비의스프링3.1] 6.6 트랜잭션 속성 (0) | 2022.03.10 |
---|---|
[토비의스프링3.1] 3.5 템플릿과 콜백 (0) | 2022.02.24 |
[토비의스프링3.1] 3.1 다시 보는 초난감 DAO (0) | 2022.02.24 |
[토비의스프링3.1] 2.5 학습 테스트로 배우는 스프링 (0) | 2022.02.24 |
[토비의스프링3.1] 1.5 스프링의 IOC (0) | 2022.01.21 |