토비의스프링 Study(3) - 템플릿

25470 단어 studystudy

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());

좋은 웹페이지 즐겨찾기