[디자인패턴] 객체지향에서의 템플릿-콜백 패턴

wakeup Lee·2023년 4월 11일
0

자바

목록 보기
6/6
post-thumbnail

템플릿-콜백 패턴이란?
1. 복잡하지만 바뀌지 않는 일정한 패턴을 갖는 작업의 흐름이 존재하고 그중 일부분만 자주 바꿔서 사용해야 하는 경우에 적합한 구조이다.
2. 전략패턴의 기본 구조에 익명 내부 클래스를 활용한 방식이다.

템플릿-콜백 패턴의 가장 전형적인 예로 try/catch/finally 블록이 적당하다. 예외상황을 처리하기 위한 catch와 리소스를 반납하거나 제거하는 finally는 자주 반복되는 상황이 발생할 수 있는데, 이때 템플릿-콜백 패턴을 적용해 볼 수 있다.

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.valueOf(line);
        }
        
        return sum;
        
    } catch (IOException e) {
    	throw e;
    } finally {
    	if (br != null) {
        	try {
            	br.close();
            } catch (IOException e) {
            	throw e;
            }
        }
    }
}
public Integer calcMultiply(String filePath) throws IOException {
	
    BufferedReader br = null;
    
    try {
    	br = new BufferedReader(new FileReader(filePath));
        
        Integer multiply = 0;
        String line = null;
        
        while ((line = br.readLine()) != null) {
        	multiply *= Integer.valueOf(line);
        }
        
        return multiply;
        
    } catch (IOException e) {
    	throw e;
    } finally {
    	if (br != null) {
        	try {
            	br.close();
            } catch (IOException e) {
            	throw e;
            }
        }
    }
}

calcSum, calcMultiply 두 함수의 역할은 각각 파일을 읽어 라인별로 더한 값과 곱한 값을 구하는 함수다.
서로 하는 역할은 다르지만 try/catch/finally 블록으로 굉장히 유사한 형태의 모습을 하고 있다. 이렇게 유사한 형태의 함수에서 중복되는 소스와 중복되지 않는 소스로 분리하여 구분할 수 있는데, 변하는 값(중복되지 않는 소스)은 하나의 템플릿으로 묶고, 변하지 않는 값(중복되는 소스)는 콜백 함수로 역할을 분리할 수 있다.

1. 변하는 값

우선 변하는 값을 찾아 콜백 인터페이스를 구현한다. 각 함수의 변하는 값은 다음과 같다.

sum += Integer.valueOf(line);
multiply *= Integer.valueOf(line);

2. 변하지 않는 값

변하지 않는 값은 변하는 값을 제외한 나머지 try/catch/finally 블록으로 *템플릿으로 분리 되어야 하는 코드다.

3. 템플릿으로 분리된 변하지 않는 값

public Integer lineReadTemplate(String filePath, LineCallback callback, Integer initVal) 
	throws IOException {

	BufferedReader br = null;
    
    try {
    	br = new BufferedReader(new FileReader(filePath));
        
        Integer result = 0;
        String line = null;
        
        while ((line = br.readLine()) != null) {
        	result = callback.doSomethingWithLine(line, result);
        }
        
        return result;
        
    } catch (IOException e) {
    	throw e;
    } finally {
    	if (br != null) {
        	try {
            	br.close();
            } catch (IOException e) {
            	throw e;
            }
        }
    }
}

lineReadTemplate 함수에 try/catch/finally 블록을 구현하였고, 변하는 값에 대해서는 콜백 함수를 전달 받아 필요한 연산을 수행할 수 있도록 하였다.

템플릿으로 분리할 때는 템플릿이 콜백에게, 콜백이 템플릿에게 전달하는 내용인지 잘 파악해야 하고, 그에 따라 콜백의 인터페이스를 정의할 수 있다.

4. 콜백함수를 구현한 변하는 값

public interface LineCallback {
    Integer doSomethingWithLine(String line, Integer value);
}
public Integer calcSum(String filePath) throws IOException {
	
        LineCallback lineCallback =
                new LineCallback<Integer>() {
                    @Override
                    public Integer doSomethingWithLine(String line, Integer value) {
                        return value += Integer.parseInt(line);
                    }
                };
        return lineReadTemplate(filePath, lineCallback, 0);
}
    public Integer calcMultiply(String filePath) throws IOException {

        LineCallback lineCallback =
                new LineCallback<Integer>() {
                    @Override
                    public Integer doSomethingWithLine(String line, Integer value) {
                        return value *= Integer.valueOf(line);
                    }
                };
        return lineReadTemplate(filePath, lineCallback, 1);
    }

LineCallback 인터페이스를 생성하여 파일의 라인별 값을 처리할 수 있는 함수를 생성했다. calcSum, calcMultiply 함수에서는 각 함수의 역할에 맞게 LineCallback 인터페이스를 구현하여 템플릿으로 파라마티를 넘겨준다.

이렇게 하면 변하는 값과 변하지 않는 값을 기준으로 각각 관심사를 분리할 수 있게 된다.

try/catch/finally 블록으로 간단한 템플릿-콜백 패턴을 알아봤는데, 코드의 특성이 바뀌는 경계를 잘 살피고 그것을 인터페이스를 사용해 분리한다는 개념으로 접근할 수 있다.

[참고자료]
토비의 스프링 3.1 (Vol.1 스프링 이해와 원리)

profile
백엔드 개발자

0개의 댓글