템플릿 메소드 패턴은 전체적으로는 동일하면서 부분적으로는 다른 구문으로 구성된 메서드의 코드 중복을 최소화할 때 유용하다.
동일한 기능을 상위 클래스에서 정의하면서 확장, 변화가 필요한 부분만 서브 클래스에서 구현할 수 있도록 한다.
// DocDataMiner
rawData = extractDocData(file)
data = parseDocData(rawData)
// CSVDataMiner
rawData = extractCSVData(file)
data = parseCSVData(rawData)
// PDFDataMiner
rawData = extractPDFData(file)
data = parsePDFData(rawData)
위 세 클래스에는 유사한 코드가 많다.
다양한 데이터 형식을 처리하는 코드는 클래스마다 다르지만 데이터 처리 및 분석을 위한 코드는 거의 같다.
알고리즘은 그대로 두고, 코드 중복을 제거하는 방법이 필요하다.
세 가지 변환 알고리즘을 일련의 단계로 나누고, 이를 메서드로 변환한다.
그리고 단일 템플릿 메서드 내부에서 위에서 변환한 메서드들을 호출한다.
다양한 변환 알고리즘을 사용하기 위해 변환 메소드는 추상화한다.
class DataMiner {
public void mine(Path path) { // 템플릿 메소드
file = openFile(path)
rawData = extractData(file)
data = parseData(rawData) // 이 메소드를 하위 클래스에서 재정의함
analysis = analyzeData(data)
sendReport(analysis)
closeFile(file)
}
protected abstract Data parseData(Data data); // 자식 클래스에서 구현 강제화
}
class PDFDataMiner extends DataMiner {
protected Data parseData(Data data) {
rawData = extractPDFData(file)
data = parsePDFData(rawData)
return data;
}
}
class CSVDataMiner extends DataMiner {
protected Data parseData(Data data) {
rawData = extractCSVData(file)
data = parseCSVData(rawData)
return data;
}
}
위 예제처럼 DataMiner
클래스의 mine
메소드는 PDFDataMiner
, CSVDataMiner
에서 동일한 기능을 구현한다.
각 자식 클래스에서 구체적으로 정의가 필요한 부분, 즉 parseData
메소드 부분만 각 하위 클래스에서 오버라이드 되도록 한다.
이러한 경우 mine
메소드를 템플릿 메소드
라고 부른다.
오버라이드할 필요가 있는 parseData
는 primitive
또는 hook
메소드라 부른다.
템플릿 메소드 패턴
템플릿 콜백 패턴
보통 다음과 같이 HttpServlet
을 상속 받아서 doGet
, doPost
를 재정의한다.
public class MyHello extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
doGet, doPost는 다음과 같이 HttpServlet
안에 service
템플릿 메소드 안에서 호출한다.
RestTemplate을 이용할때 exchange
를 이용해 요청을 보낸다.
// RestTemplate
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.set("X-COM-PERSIST", "NO");
headers.set("X-COM-LOCATION", "USA");
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<String> responseEntity = restTemplate
.exchange("http://localhost:8080/users", HttpMethod.GET, entity, String.class);
다음과 같이 doExceute
템플릿 메소드에서 재정의한 execute
를 사용한다.