Today I Learned_230704
토비의 스프링 3.1
executeSql은 UserDao말고 다른 Dao 클래스에서도 사용성이 높아 보인다 → 템플릿 클래스로 빼자
// JdbcContext
public void executeSql(final String query) throws SQLException {
this.workWithStatementStrategy(
new StatementStrategy() {
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
return c.prepareStatement(query);
}
}
);
}
// UserDao의 deleteAll
private void deleteAll() throws SQLException {
this.jdbcContext.executeSql("delete from users");
}
→ JdbcContext 안에 클라이언트(executeSql), 템플릿(workWithStatementStrategy), 콜백(익명 내부 클래스)이 모두 함께 공존하면서 동작하는 구조
일반적으로 성격이 다른 코드들은 가능한 한 분리하는 편이 낫지만, 이 경우 하나의 목적을 위해 서로 긴밀하게 연관되어 동작하는 응집력이 강한 코드들이므로 모여 있는 것이 유리하다.
구체적인 구현, 내부의 전략 패턴, 코드에 의한 DI, 익명 내부 클래스 등의 기술은 최대한 감춰두고, 외부에는 꼭 필요한 기능을 제공하는 단순 메소드만 노출한다.
템플릿/콜백의 응용
템플릿/콜백은 스프링만의 기술은 아니지만, 스프링을 잘 활용하려면 스프링에서 제공하는 템플릿/콜백 기능을 잘 사용할 수 있어야 한다.
언제 템플릿/콜백 패턴을 사용할 수 있을지?
→ 고정된 작업 흐름(컨텍스트)을 갖고 반복되는 코드(템플릿)가 있다면 중복되는 코드를 분리하자.
중복 코드는 우선 메소드로 분리 → 전략 패턴 적용하여 DI로 의존관계 관리
→ 바뀌는 부분이 한 애플리케이션에서 동시에 여러 종류로 만들어진다 → 템플릿/콜백 패턴
try/catch/finally가 반복되면 템플릿/콜백 패턴을 적용해 볼 수 있다.
try/catch를 템플릿/콜백 패턴으로 바꿔보기
예시 코드
package com.example.demo3.learningtest.template;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.assertThat;
import java.io.IOException;
public class CalcSumTest {
@Test
public void sumOfNumbers() throws IOException {
Calculator calculator = new Calculator();
int sum = calculator.calcSum("numbers.txt");
assertThat(sum, is(10));
}
}
try-catch가 포함된 Calculator → 덧셈 기능을 하는 calcSum이 있는데 try-catch가 너무 많다.
package com.example.demo3.learningtest.template;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Calculator {
public Integer calcSum(String filepath) throws IOException {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(filepath));
Integer sum = 0;
String line = null;
while((line = br.readLine()) != null) {
sum += Integer.parseInt(line);
}
br.close();
return sum;
}
catch (Exception e) {
System.out.println(e.getMessage());
throw e;
}
finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
}
}
곱하기 기능을 넣으려면 이 모든 try-catch를 복사/붙여넣기 해야 하는데 너무 귀찮을 것 같다!
→ 템플릿/콜백 패턴을 적용해 보자.
// 템플릿/콜백을 적용한 메소드
public Integer calcSum(String filepath) throws IOException {
return fileReadTemplate(filepath, new BufferedReaderCallback() {
@Override //공통되지 않은 부분을 뺐다
public Integer doSomethingWithReader(BufferedReader br) throws IOException {
Integer sum = 0;
String line = null;
while((line = br.readLine()) != null) {
sum += Integer.parseInt(line);
}
return sum;
}
});
}
// 템플릿 메소드
public Integer fileReadTemplate(String filepath, BufferedReaderCallback callback) throws IOException {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(filepath));
int ret = callback.doSomethingWithReader(br); // 파라미터를 담아 콜백 오브젝트를 호출하고, 콜백의 작업 결과를 받아둔다
br.close();
return ret;
}
catch (Exception e) {
System.out.println(e.getMessage());
throw e;
}
finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
}
곱셈을 구하는 메소드를 구현하려면 calSum에서 익명 메소드 부분을 수정하면 된다.