특정 작업을 처리하는 일부분을 서브 클래스로 캡슐화하여 전체적인 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내용을 바꾸는 패턴이다.
- 전체적으로는 동일하면서 부분적으로는 다른 구문으로 구성된 메서드의 코드 중복을 최소화할 때 유용하다.
- 다른 관점에서 보면 동일한 기능을 상위 클래스에서 정의하면서 확장/변화가 필요한 부분만 서브 클래스에서 구현할 수 있도록 한다.
- 예를 들어, 전체적인 알고리즘은 상위 클래스에서 구현하면서 다른 부분은 하위 클래스에서 구현할 수 있도록 함으로써 전체적인 알고리즘 코드를 재사용하는데 유용하도록 한다.

AbstractClass :
- 템플릿 메서드를 정의하는 클래스
- 하위 클래스에 공통 알고리즘을 정의하고, 하위 클래스에서 구현될 기능을 primitive메서드 또는 hook 메서로 정의하는 클래스
ConcreteClass :
- 물려받은 primitive 메서드 또는 hook 메서드를 구현하는 클래스
- 상위 클래스에 구현된 템플 메서드의 일반적인 알고리에서 하위 클래스에 적합하게 primitive 메서드나 hook 메서드를 오버라이드하는 클래스

다음과 같이 Competitor1, Competitor2 .... 는 AbstractCompetitor를 상속받는다.
AbstractClass
public abstract class AbstractCompetitor implements ICompetitor, RequestUtil {
private LocalDateTime cronAt;
private int connectionTimeout = 30;
-
public Response call(String name, String url) {
Response response = null;
ChromeDriver chromeDriver = null;
...... 생략
return response;
}
public JSONObject sendPost(@NonNull String targetUrl, @NonNull Map<String, String> headers, @NonNull String param) throws IOException {
String response = sendPost(new URL(targetUrl), headers, param);
JSONObject jsonObject = null;
if (isJsonValid(response) == true) {
jsonObject = parseStrToJson(response);
}
return jsonObject;
}
public String sendPost(@NonNull URL url, @NonNull Map<String, String> headers, @NonNull String param) throws IOException {
//URL url = new URL(targetUrl);
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
...... 생략
return response.toString();
}
public JSONObject sendGet(@NonNull String targetUrl) throws IOException {
JSONObject jsonObject = null;
...... 생략
return jsonObject;
}
public String sendGet(@NonNull URL url) throws IOException {
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
...... 생략
return response.toString();
}
public JSONObject sendGet(@NonNull String targetUrl, @NonNull Map<String, String> headers, String param) throws IOException {
if (!isEmpty(targetUrl)) {
targetUrl = targetUrl.concat("?");
}
JSONObject jsonObject = null;
...... 생략
return jsonObject;
}
public String sendGet(@NonNull URL url, @NonNull Map<String, String> headers, String param) throws IOException {
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setConnectTimeout(connectionTimeout * 1000);
con.setRequestMethod("GET");
con.setDoOutput(true);
...... 생략
return response.toString();
}
public JSONObject requestDataByGET(String name, String requestUrl, Map<String, String> headers) throws IOException, ParseException, NullPointerException {
RequestMethodGet requestMethodGet = new RequestMethodGet(name, requestUrl, headers);
...... 생략
return (JSONObject) response.getData();
}
public JSONObject requestDataByGET(String name, String requestUrl) throws IOException, ParseException, NullPointerException {
...... 생략
return (JSONObject) response.getData();
}
public JSONObject requestDataByPOST(String name, String requestUrl, Map<String, String> headers, String urlEncode) throws IOException, ParseException, NullPointerException {
...... 생략
return (JSONObject) response.getData();
}
private BufferedReader getBufferReader(InputStream inputStream) throws UnsupportedEncodingException {
return new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
}
public String urlEncode(String url, Map<String, String> map) {return url.concat("?").concat(urlEncode(map));}
public String urlEncode(Map<String, String> map) {
...... 생략
return sb.toString();
}
public List<Response> threadRun(String name, List<?> list) {
if (isEmpty(list)) {
Logger.error("{}, parameter is null", name);
return null;
}
...... 생략
return responseList;
}
@Override
public List<?> createRequestData() throws IOException, ParseException, java.text.ParseException {return null;}
@Override
public Response createStoredData(List<?> list, String cronAt, String name) {return null;}
@Override
public Map<String, String> getHeaders(ChromeDriver chromeDriver, String url) {return null;}
private ChromeDriver chromeOpen(String url) {
...... 생략
return chromeDriver;
}
private void chromeClose(ChromeDriver chromeDriver) {
if (!isEmpty(chromeDriver)) {
chromeDriver.close();
}
}
public void setCronAt(LocalDateTime cronAt) {this.cronAt = cronAt;}
public LocalDateTime getCronAt() {return this.cronAt;}
}
ConcreteClass
public class Competitor1 extends AbstractCompetitor {
...... 생략
@Override
public Response call() {return super.call(name, url);}
@Override
public Response request() throws IOException, ParseException, NullPointerException {
return null;
}
@Override
public Response request(ChromeDriver chromeDriver) {
try {
LocalDateTime now = super.getCronAt();
String cronAt = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// 1. create Request Data
List<RequestMethodGet> list = (List<RequestMethodGet>) createRequestData();
// 2. Processing stored data
return createStoredData(list, cronAt, name);
} catch (Exception e) {
e.getStackTrace();
Logger.error("{}, exception e={}", name, e.getMessage());
return new Response(RESPONSE_FAIL.CODE(), "fail", null, name);
} finally {
ChromeManager.close(this.chromeDriver);
}
}
@Override
public List<?> createRequestData() {
List<RequestParam> list = seleniumConfigService.getParameters(name);
...... 생략
return requestDataList;
}
@Override
public Response createStoredData(List<?> list, String cronAt, String name) {
...... 생략
return new Response(RESPONSE_SUCCESS.CODE(), "success", response, name);
}
public Map<String, String> getHeaders(ChromeDriver chromeDriver, String url) {
Cookie cookie = chromeDriver.manage().getCookieNamed("csrftoken");
...... 생략
return headers;
}
}
실제 개발했던 프로젝트를 예로 들었다.
결론적으로 공통코드는 AbstractClass 에서 구현하고, 확장/변화가 필요한 부분만 서브 클래스에서 구현하도록 한다.
장점 :
- 중복코드를 줄일 수 있다.
- 자식 클래스의 역할을 줄여 핵심 로직의 관리가 용이하다.
- 객체지향적인 구성을 할 수 있다.
단점 :
- 추상 메서드가 많아지면서 클래스 관리가 복잡해진다.
- 클래스간의 관계와 코드가 꼬여버릴 염려가 있다.
참고 :
https://gmlwjd9405.github.io/2018/07/13/template-method-pattern.html
[Design Pattern] 템플릿 메서드 패턴이란 - Heee's Development Blog
Step by step goes a long way.
gmlwjd9405.github.io
https://coding-factory.tistory.com/712
[Design Pattern] 템플릿 메소드 패턴(Template Method Pattern)에 대하여
템플릿 메소드 패턴(Template Method Pattern)이란? 템플릿 메소드 패턴이란 특정 작업을 처리하는 일부분을 서브 클래스로 캡슐화하여 전체적인 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내용
coding-factory.tistory.com
| [Design Pattern] Factory Method Pattern (0) | 2023.03.19 |
|---|---|
| [Design Pattern] Observer Pattern (0) | 2023.03.18 |
| [java] ThreadPool (1) | 2023.02.23 |
| [Java] Map 동시성 (1) | 2022.12.22 |
| [jwt] Signed Claims JWSs are not supported. (0) | 2022.11.15 |