디자인 패턴
템플릿 콜백 패턴을 이해하기 위해 기초적인 디자인 패턴에 대해 먼저 정리한다.
템플릿 메소드 패턴
토비의 스프링에서는 템플릿 메소드 패턴을 아래와 같이 정의한다.
템플릿 메소드 패턴은 상속을 통해 기능을 확장해서 사용하는 부분이다. 변하지 않는 부분은 슈퍼클래스에 두고 변하는 부분은 추상 메소드로 정의해둬서 서브클래스에서 오버라이드하여 새롭게 정의해 쓰도록 하는 것이다.
핵심을 세 문장으로 정리할 수 있다.
•
슈퍼클래스에 변하지 않는 부분을 템플릿 메소드로 정의한다.
•
슈퍼클래스에 변하는 부분을 추상메소드로 정의한다.
•
서브클래스에서 변하는 부분을 새롭게 정의하여 사용한다.
전체적인 구조
•
AbstractClass는 변하지 않는 부분(hook1(), hook2()를 연이어 호출하는 방식)을 templateMethod()에 정의한다.
•
AbstractClass는 변하는 부분(hook1(), hook2()의 내용)을 추상메소드로 정의한다.
•
ConcreteClass에서 변하는 부분인 hook1(), hook2()를 오버라이드해 사용한다.
예제와 장단점
전략 패턴
토비의 스프링에서는 전략 패턴을 아래와 같이 정의한다.
…, 오브젝트를 아예 둘로 분리하고 클래스 레벨에서는 인터페이스를 통해서만 의존하도록 만드는 전략 패턴이다. 전략 패턴은 OCP 관점에 보면 확장에 해당하는 변하는 부분을 별도의 클래스로 만들어 추상화된 인터페이스를 통해 위임하는 방식이다.
핵심을 다음과 같이 정리할 수 있다.
•
변하는 것과 변하지 않는 것이 모두 담긴 클래스를 두 개의 클래스로 분리한다.
•
하나의 클래스(Context)는 변하지 않는 것을 담는다.
•
변하는 것은 인터페이스(Strategy)와 인터페이스를 구현한 클래스(ConcreteStrategy)에 담는다.
•
변하지 않는 클래스(Context)에서 변하는 기능을 사용할 때는 인터페이스를 통해 외부의 독립된 클래스(ConcreteStrategy)에 위임한다.
Context와 Strategy
전략패턴에서는 Context와 Strategy라는 용어가 등장한다.
•
Context는 변하지 않는 일정한 작업을 수행하는 클래스이다. 복잡하지만 바뀌지 않는 일정한 작업의 흐름, 이것이 맥락(context)이다.
•
Context 내부에는 변하는 부분이 있는데 이것은 의도에 따라 바뀔 수 있는 전략(Strategy)이다.
정리하자면,
전략패턴은 변하지 않는 어떤 맥락(Context)에서 변하는 부분(Strategy)만 따로 정의하고 기능을 수행하도록 하는 디자인 패턴이다.
Context에 Strategy를 주입하기
Context는 필요에 따라 서로 다른 Strategy에게 필요한 기능을 위임한다. 그를 위해 Context 클래스 내부에서 참조할 ConcreteStrategy 객체가 필요하다. 그렇다면 Context에 ConcreteStrategy 객체는 어떻게 생성될 수 있을까.
•
일반적인 방식으로 객체를 생성한다면 전략패턴의 적절한 사용이라 할 수 없다.
•
만약 어떤 Context 내에 다음과 같은 ConcreteStrategyA를 생성하는 코드가 있다면, Context는 ConcreteStrategyB를 사용하고 싶을 때 코드를 수정해야만 한다.
class Context{
...
Strategy strategyA = new ConcreteStrategyA();
...
}
Java
복사
•
따라서 Context를 사용하는 Client가 구체적인 전략 중 하나를 선택하고 오브젝트로 만들어서 Context에 주입하는 것이 타당하다.
•
Context는 Client에게 생성자, setter 함수 등으로 Strategy를 주입받을 수 있다.
class Context{
...
Strategy strategy
public Context(Strategy strategy) {
this.strategy = strategy;
}
...
}
Java
복사
예시
템플릿 콜백 패턴
템플릿 콜백 패턴은 전략 패턴의 응용 방식이라 볼 수 있다.
토비의 스프링은 템플릿/콜백 패턴을 아래와 같이 정의한다.
전략 패턴의 기본 구조에 익명 내부 클래스를 활용하는 방식을 스프링에서는 템플릿/콜백 패턴이라고 부른다. 전략 패턴의 Context를 템플릿이라 부르고, 익명 내부 클래스로 만들어지는 오브젝트를 콜백이라고 부른다.
핵심을 다음과 같이 정리할 수 있다.
•
템플릿 콜백 패턴은 전략 패턴의 응용이다.
•
변하지 않는 부분인 템플릿에 변하는 부분을 익명 내부 클래스(콜백 방식)로 기능하게 한다.
템플릿 콜백의 작업 흐름
•
클라이언트는 콜백 오브젝트를 만들고, 템플릿 메소드를 호출할 때 콜백을 전달한다.
•
템플릿은 정해진 작업 흐름 중 콜백 오브젝트의 메소드를 호출한다. 콜백은 클라이언트 메소드의 정보와 템플릿이 제공한 참조정보를 이용해서 작업을 수행하고 그 결과를 다시 템플릿에 돌려준다.
•
템플릿은 콜백이 돌려준 정보를 사용해 작업을 마친다.
DI 방식의 전략 패턴 구조라고 생각하면 간단하다!
예제
코드 리뷰
1.
토비 스프링 261p
•
변하지 않는 부분을 분리시킴
// UserDao.java
public void deleteAll() throws SQLException, ClassNotFoundException {
executeSql("delete from users"); //쿼리만 함수 인자로 넘김
}
private void executeSql(final String query) throws SQLException{
this.jdbcContext.workWithStatementStartegy(new StatementStartegy() {
@Override
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
return c.prepareStatement(query);
}
});
}
}
Java
복사
•
deleteAll() 함수에 쿼리만 남기도록 하기 위해 executeSql함수 새로 작성
2.
콜백과 템플릿의 결합
•
JdbcContext로 executeSql()함수를 옮김
//JdbcContext.java
public void executeSql(final String query) throws SQLException{
workWithStatementStartegy(new StatementStartegy() {
@Override
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
return c.prepareStatement(query);
}
});
}
Java
복사
//UserDao.java
public void deleteAll() throws SQLException, ClassNotFoundException {
this.jdbcContext.executeSql("delete from users"); //JdbcContext에서 호출
}
Java
복사
3.
스프링 jdbcTemplate 적용
•
jdbcTemplate을 통해서 더욱 간편하게 DB와 연동이 가능하다
// UserDao.java
public UserDao(DataSource datasource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
Java
복사
// UserDao.java
public void add(final User user) {
this.jdbcTemplate.update("insert into users(id,name,password) values (?, ?, ?);",
user.getId(),user.getName(),user.getPassword());
}
Java
복사
•
add함수 역시 jdbcTemplate을 통해 한번에 가능
//UserDao.java
public void deleteAll() throws SQLException, ClassNotFoundException {
this.jdbcTemplate.update("delete from users");
}
Java
복사
•
deltaAll()함수에서 SQL 구문인 "delete from users" 로 jdbcTemplate.update 함수를 실행한다.
//UserDao.java
public int getCount() throws SQLException, ClassNotFoundException {
return this.jdbcTemplate.queryForObject("select count(*) from users;", Integer.class);
}
Java
복사
//UserDao.java
public User findById(String id) throws ClassNotFoundException {
String sql = "select * from users where id = ?";
RowMapper<User> rowMapper = new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User(rs.getString("id"), rs.getString("name"),
rs.getString("password"));
return user;
}
};
return this.jdbcTemplate.queryForObject(sql, rowMapper, id);
}
Java
복사
•
RowMapper
◦
RowMapper의 개체를 생성하여 mapRow() 메서드를 통해 ResultSet에서 한 행의 데이터를 읽어와 자바 객체로 변환하는 매퍼 기
//UserDao.java
public List<User> getAll(){
String sql = "select * from users order by id;";
RowMapper<User> rowMapper = new RowMapper<User>() {
public User mapRow(ResultSet rs, int rowNum) throws SQLException{
User user = new User(rs.getString("id"),rs.getString("name"),rs.getString("password"));
return user;
}
};
return this.jdbcTemplate.query(sql,rowMapper);
}
Java
복사
Algorithm
commands[i] = [ fromIndex, toIndex, From~to 로 정렬한 수 중 n번째 숫자 ]
배열로 풀이
우선순위 큐 자료구조로 풀이
마무리
java와 spring을 사용해서 DAO(Data Access Object)클래스를 작성하고, User 데이터의 CRUD(생성, 읽기, 갱신, 삭제) 작업을 메소드로 구현해 보았다.
다음 진도부턴 Spring을 더 쉽게 사용하기 위한 도구, Spring Boot를 이용하여 개발하는 방법을 배우게 될 것이다!