상세 컨텐츠

본문 제목

[Design Pattern] Template Method Pattern

JAVA

by jeonghojin 2023. 3. 17. 02:40

본문

Template Method pattern

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

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 에서 구현하고, 확장/변화가 필요한 부분만 서브 클래스에서 구현하도록 한다.


 

Template Method Pattern 장점 / 단점

 
장점 :
- 중복코드를 줄일 수 있다.
- 자식 클래스의 역할을 줄여 핵심 로직의 관리가 용이하다.
- 객체지향적인 구성을 할 수 있다.
 
단점 : 
- 추상 메서드가 많아지면서 클래스 관리가 복잡해진다.
- 클래스간의 관계와 코드가 꼬여버릴 염려가 있다.


참고 : 

'JAVA' 카테고리의 다른 글

[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

관련글 더보기