토비의스프링 Study(3) - 템플릿
3.1 다시보는 초난감DAO
직전의 DAO코드에서는 JDBC에 예외상황에대해 다루지않았던 문제가 존재
try-catch-finally 로 일단 덮어씌우기
public void deleteAll() throws SQLException {
Connection c = null;
PreparedStatement ps = null;
try {
c = dataSource.getConnection();
ps = c.prepareStatement("delete from users");
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) {
}
}
}
}
3.2 변하는것과 변하지않는것
try/catch/finally 블록이 모든메소드에 2중으로 중첩되어있는문제가 존재
deleteAll 뿐아니라, 다른로직에도 똑같이 복붙을 할때 복붙 실수를 한다던지 close()처리를 빼먹는등의 실수를 할경우 문제해결을 하는것이 쉬운일이아니다.
템플릿 메소드패턴 (상속) 적용
// AbstractClass
public abstract class UserDao{
public void deleteAll() throws SQLException{
try{
...
ps = makeStatement(c); // ps= c.prepareStatement("delete from users");
...
}catch ...
}
//hook1
abstract protected PreparedStatement makeStatement(Connection c) throws SQLException;
}
// ConcreteClass
public class UserDaoDeleteAll extends UserDao{
//hook1
@Override
protected PreparedStatement makeStatement(Connection c) throws SQLException {
return c.prepareStatement("delete from users");
}
}
}
장점
- 기능을 확장하고싶을때 자유롭게 확장이 가능해짐
- 템플릿코드를 재사용하여 중복을 줄일수있다.
단점
- DAO의 로직마다 새로운클래스를 생성해야함(add,delete,get,getcount ....)
- 확장구조가 컴파일시점에 고정되어, 유연성이 떨어짐
전략패턴 적용
변하지않는 , 일정한 구조를가지고있는부분 에서 특정 확장기능이 필요하다면 인터페이스를통해 외부의 독립된 전략클래스에 위임하는것
PreparedStatement 를 만들어줄 외부기능호출(전략)
//Strategy
public interface StatementStrategy {
PreparedStatement makePreparedStatement(Connection c) throws SQLException;
}
//ConcreteStrategy(1)
public class DeleteAllStatement implements StatementStrategy{
@Override
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
return c.prepareStatement("delete from users");
}
}
// context
void deleteAll() {
try{
...
StatementStrategy strategy=new DeleteAllStatement();
ps = strategy.makePreparedStatement(c);
}
}
기본 컨텍스트는 그대로 유지되면서 전략만 바꿔서 사용한다.
문제점
- 컨텍스트 안에서 이미 구체적인 전략클래스인 DeleteAllStatement, StatementStrategy 인터페이스 가 의존되어있음
=> 전략패턴, OCP원칙 둘다 들어맞지않는다고 볼수있다.
컨텍스트 안에 클라이언트 코드가 같이 들어있어서 생기는 문제
클라이언트/context 분리
/**
* 클라이언트
*/
public void deleteAll() throws SQLException {
StatementStrategy st = new DeleteAllStatement(); //전략 클래스
jdbcContextWithStatementStrategy(st);
}
/**
* 클라이언트로부터 전략오브젝트를 제공받은, 컨텍스트가 담긴 메서드
*/
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) {
...
}
}
3.3 JDBC전략 패턴의 최적화, 3.4 컨텍스트와 DI
인터페이스로
AddStatement , DeleteAllStatement를 만들게되고, 계속 클래스파일이 늘어난다는점때문에
로컬클래스 -> 새로운 전략클래스들을 익명클래스로 전환
public void deleteAll() throws SQLException {
this.jdbcContext.workWithStatementStrategy(
new StatementStrategy() {
@Override
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
return c.prepareStatement("delete from users");
}
}
);
}
다른DAO에서도 jdbc컨텍스트를 사용할수있게
밖으로 독립, jdbcContext를 DI받음
UserDao{
private JdbcContext jdbcContext;
public void setJdbcContext(JdbcContext jdbcContext) {
this.jdbcContext = jdbcContext;
}
...
}
3.5 템플릿과 콜백
템플릿(context)
콜백(익명클래스)
=> 특정 로직을 담은 메소드를 실행시키기위해 전달되는 오브젝트
public void deleteAll() throws SQLException {
this.jdbcContext.workWithStatementStrategy(
new StatementStrategy() {
@Override
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
return c.prepareStatement("delete from users");
}
}
);
}
sql쿼리(변하는부분)
콜백클래스 정의(변하지않는부분) 분리
public void deleteAll() throws SQLException {
executeSql("delete from users");
}
private void executeSql(final String query) throws SQLException {
this.jdbcContext.workWithStatementStrategy(
new StatementStrategy() {
@Override
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
return c.prepareStatement(query);
}
}
);
}
중복코드 메소드분리 -> 인터페이스로 전략패턴 - 바뀌는부분이 여러종류가 만들어질수있다면, 템플릿/콜백 패턴 적용 하는걸 고려하라
템플릿 콜백패턴
콜백이라는 인터페이스 사용, 상속을 사용하지않아도 된다는 장점이있음
3.6 스프링의 JdbcTemplate
스프링의 JdbcTemplate을 직접 적용해봄
JdbcTemplate.update() - deleteAll()
public void deleteAll(){
this.jdbcTemplate.update("delete from users;");
}
JdbcTemplate.update() - add()
this.jdbcTemplate.update("insert into users(id, name, password) values(?,?,?)", user.getId(), user.getName(), user.getPassword());
Author And Source
이 문제에 관하여(토비의스프링 Study(3) - 템플릿), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@dudwls0505/토비의스프링3-템플릿저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)