에라토스테네스 체
: 주어진 숫자 범위에서 2의 배수, 3의 배수, 5의 배수, 7의 배수를 제거하고 남은 수가 소수다arr = ['a', 'b', 'c']
for i in range(0, len(arr)):
print(list(permutations(arr, i)))
[()]
[('a',), ('b',), ('c',)]
[('a', 'b'), ('a', 'c'), ('b', 'a'), ('b', 'c'), ('c', 'a'), ('c', 'b')]
바뀌지 않는 부분은 컨텍스트로, 바뀌는 부분은 전략으로
하나의 코드 뭉치에 변경되지 않는 부분과 변경 되는 부분의 경계가 뚜렷하면 변화의 특성이 다른 것이다.
자주 변경 되는 부분을 전략 객체로 분리하면 좋다.
자주 변하는 부분을 변하지 않는 부분과 독립적으로 변경될 수 있게 만드는 것이다.
전략 패턴을 적용하려면 자꾸 확장되고 자주 변하는 코드를 잘 분리해내야 한다.
템플릿/콜백 패턴으로 변경하면 코드는 아래와 같다.
[템플릿 메서드]
// 잘 변하지 않는 부분을 템플릿 역할을 하도록 메서드로 분리했다.
public <T> T fileReaderTemplate(String filePath, BufferedReaderCallback<T> callback) throws IOException {
BufferedReader br = null;
try {
// 자주 변경되는 부분을 전략 인터페이스로 분리하고 외부에서 전략 객체를 주입 받아 작업을 요청한다.
return callback.doSomethingWithReader(new BufferedReader(new FileReader(filePath)));
} catch (IOException e) {
System.out.println(e.getMessage());
throw e;
} finally {
if (br != null) {
try {
System.out.println("resource closed");
br.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
}
[전략 인터페이스]
// 전략 객체들의 인터페이스
public interface BufferedReaderCallback<T> {
T doSomethingWithReader(BufferedReader bufferedReader) throws IOException;
}
[클라이언트 메서드]
public Integer calcSum(String filePath) throws IOException, NumberFormatException {
// 템플릿 메서드를 호출하면서 전략 객체를 익명 내부 클래스로 전달하고 있다.
return fileReaderTemplate(filePath, new BufferedReaderCallback() {
@Override
public Integer doSomethingWithReader(BufferedReader br) throws IOException {
Integer sum = 0;
String line = null;
while ((line = br.readLine()) != null) {
sum += Integer.valueOf(line);
}
return sum;
}
});
}
전략 패턴은 반복되는 코드에 특정 부분반 자주 바꿔서 사용해야 하는 경우에 적합한 구조다.
전략 객체를 인터페이스 구현클래스로 별도로 두지 않고 익명 내부 클래스를 활용한 방식을
템플릿/콜백 패턴이라고 부른다.
이때는 전략 패턴의 컨텍스트를 템플릿이라 부르고, 익명 내부 클래스로 만들어지는 오브젝트를 콜백이라 한다.
익명 내부 클래스의 로직을 파라미터 변수로 전달할 수 있다.
템플릿 내에서 콜백 함수를 중첩으로 호출할 수도 있으며 콜백 인터페이스를 제네릭 타입으로 변경해 재사용성을 증가시킬 수도 있다.
스프링은 이렇게 다양하게 템플릿/콜백 패턴을 응용해서 사용하고 있다.
JdbTemplate 은 템플릿/콜백 패턴으로 구현된 클래스다
클라이언트에게 전달 받은 SQL 을 실행하고, 결과를 추출하고, 예외를 처리해준다.
PreparedStatementCreator
: statement 를 생성하는 콜백 인터페이스다.
ResultSetExtractor
: ResultSet 으로 부터 결과를 추출하는 콜백이다.
JdbTemplate 내부를 잠시 살펴보면 콜백을 통해 작업을 요청하고 결과값을 리턴한다.
sql
를 매개변수로 받아서 내부에서 QueryStatementCallback
콜백 객체를 생성해 execute()
로 넘긴다.
QueryStatementCallback
이 수행하는 작업은 쿼리 실행 후 실행 결과를 ResultSetExtractor
콜백에게 처리를 위임해 그 결과를 리턴한다.
커넥션을 취득하고 StatementCallback
에게 쿼리 수행을 요청하고 그 결과를 반환한다.
execute()
의 변하지 않는 부분은 커넥션을 취득하고 쿼리 수행 중 에러가 발생하면 예외 처리를 하는 것이다. 쿼리 수행 부분을 콜백에게 위임했다.
쿼리 수행, 쿼리 결과 처리 코드가 각각 StatementCallback
, ResultSetExtractor
콜백 인터페이스로 분리되면서 언제든 변경과 확장이 가능해졌다.
StatementCallback
구현체만 보더라도 5개가 있다. execute()
메서드는 Statement 어떤 방식으로 처리 되는지 알 필요가 없고 작업 수행을 위한 커넥션 준비와 예외 발생시 예외 처리에만 집중할 수 있게 되었다.
코드를 변경하더라도 Statement 에는 영향이 가지 않는다.