템플릿 메소드 (Template method) 패턴
알고리즘 구조를 서브 클래스가 확장할 수 있도록 템플릿으로 제공하는 방법
추상 클래스는 템플릿을 제공하고 하위 클래스는 구체적인 알고리즘을 제공한다
템플릿 메소드 (Template method) 패턴 before
FileProcessor 클래스
public class FileProcessor {
private String path;
public FileProcessor(String path) {
this.path = path;
}
public int process(){
try(BufferedReader br = new BufferedReader(new FileReader(path))) {
int result = 0;
String line = null;
while ((line = br.readLine()) != null){
result += Integer.parseInt(line); // parseInt: int valueOf: Integer
}
return result;
}
catch (IOException e){
throw new IllegalArgumentException(path + "에 해당하는 파일이 없습니다.", e);
}
}
}
process() 메소드: 생성자로 주어지는 path에 해당하는 파일을 읽어와서 더한다.
number.txt
Client
public class Client {
public static void main(String[] args) {
FileProcessor fileProcessor = new FileProcessor("number.txt");
int result = fileProcessor.process();
System.out.println(result);
}
}
실행결과: 15
만약 읽어온 숫자들을 더하는 게 아닌 곱하기를 하고 싶다면?
MultiplyFileProcessor
public class MultiplyFileProcessor {
private String path;
public MultiplyFileProcessor(String path) {
this.path = path;
}
public int process(){
try(BufferedReader br = new BufferedReader(new FileReader(path))) {
int result = 1;
String line = null;
while ((line = br.readLine()) != null) {
result *= Integer.parseInt(line);
}
return result;
}
catch (IOException e){
throw new IllegalArgumentException(path + "에 해당하는 파일이 없습니다.", e);
}
}
}
FileProcessor와 중복인 코드가 많다.
템플릿 메소드 패턴을 적용해서 중복 코드를 추상 클래스의 템플릿 메소드로 만들고 각각의 알고리즘(더하기, 곱하기)을 하위 클래스에서 구현하도록 만들어 보자
템플릿 메소드 (Template method) 패턴 after
abstract class FileProcessor
public abstract class FileProcessor {
private String path;
public FileProcessor(String path) {
this.path = path;
}
public final int process(int init){
try(BufferedReader br = new BufferedReader(new FileReader(path))) {
int result = init;
String line = null;
while ((line = br.readLine()) != null){
result = getResult(result, Integer.parseInt(line));
}
return result;
}
catch (IOException e){
throw new IllegalArgumentException(path + "에 해당하는 파일이 없습니다.", e);
}
}
protected abstract int getResult(int result, int value);
}
템플릿 메소드인 process 정의
하위 클래스에서 구체적인 알고리즘인 getResult를 구현하도록 만들었다.
즉, 알고리즘의 구조(큰 틀)를 정의하면서, 일부 구체적인 부분은 하위 클래스에서 구현하도록 강제하였다.
Plus, Multiply
public class Plus extends FileProcessor{
public Plus(String path) {
super(path);
}
@Override
protected int getResult(int result, int value) {
return result += value;
}
}
public class Multiply extends FileProcessor{
public Multiply(String path) {
super(path);
}
@Override
protected int getResult(int result, int value) {
return result *= value;
}
}
추상 클래스인 FileProcessor 상속 받은 Plus, Multiply
구체적인 알고리즘을 getResult에 구현
Client
public class Client {
public static void main(String[] args) {
Multiply multiply = new Multiply("number.txt");
Plus plus = new Plus("number.txt");
System.out.println(multiply.process(1)); // 120
System.out.println(plus.process(0)); // 15
}
}
템플릿 콜백 (Template-Callback) 패턴
콜백으로 상속 대신 위임을 사용하는 템플릿 패턴
상속 대신 익명 내부 클래스 또는 람다 표현식을 활용할 수 있다
템플릿 콜백 (Template-Callback) 패턴 적용
Operator - Callback
public interface Operator {
abstract int getResult(int result, int number);
}
FileProcessor
public class FileProcessor {
private String path;
public FileProcessor(String path) {
this.path = path;
}
public final int process(int init, Operator operator){
try(BufferedReader br = new BufferedReader(new FileReader(path))) {
int result = init;
String line = null;
while ((line = br.readLine()) != null){
result = operator.getResult(result, Integer.parseInt(line));
}
return result;
}
catch (IOException e){
throw new IllegalArgumentException(path + "에 해당하는 파일이 없습니다.", e);
}
}
}
FileProcessor의 템플릿 메소드 process는 이제 Operator 즉, 콜백(CallBack)을 매개변수로 받고
내부에서 콜백의 메소드를 사용한다.
Client
public class Client {
public static void main(String[] args) {
FileProcessor fileProcessor = new FileProcessor("number.txt");
int plusResult = fileProcessor.process(0, ((result, number) -> result += number));
System.out.println("plusResult = " + plusResult); // 15
int multiplyResult = fileProcessor.process(1, ((result, number) -> result *= number));
System.out.println("multiplyResult = " + multiplyResult); // 120
}
}
이렇게 Concrete Callback을 구현할 필요 없이 런타임에 익명 클래스나 람다 표현식을 통해서 process에 넘겨주면 끝이다.
템플릿 콜백 패턴 VS 전략 패턴
특징 | 템플릿 콜백 패턴 | 전략 패턴 |
구현해야 하는 메소드 | 1개 메소드 (콜백으로 전달됨) | 여러 개의 메소드 (각 전략 클래스가 자체적으로 알고리즘을 구현) |
알고리즘의 구조 | 알고리즘의 흐름은 고정, 일부 동작만 변경 가능 | 알고리즘 전체를 독립적인 전략 객체로 캡슐화, 동적으로 교체 가능 |
변경 용이성 | 특정 동작만 변경 가능 | 알고리즘 전체를 교체 가능 |
유연성 | 유연성이 낮음 (동작을 바꾸는 것이 제한적) |
유연성 높음 (알고리즘을 자유롭게 교체) |
참고로 템플릿 콜백 패턴은 GoF가 정의한 디자인 패턴은 아니다.
템플릿 메소드 (Template method) 패턴 장단점
장점
템플릿 코드를 재사용하고 중복 코드를 줄일 수 있다
템플릿 코드를 변경하지 않고 상속을 받아서 구체적인 알고리즘만 변경할 수 있다
단점
리스코프 치환 원칙을 위반할 수도 있다
알고리듬 구조가 복잡할 수록 템플릿을 유지하기 어려워진다
'Computer Sience > Desgin Pattern' 카테고리의 다른 글
[Design Pattern] 전략 (Strategy) 패턴 (1) | 2024.11.25 |
---|---|
[Design Pattern] 상태(State) 패턴 (1) | 2024.11.25 |
[Design Pattern] 옵저버(Observer) 패턴 (0) | 2024.11.25 |
[Design Pattern] 메멘토(Memento) 패턴 (1) | 2024.11.07 |
[Design Pattern] 중재자(Mediator) 패턴 (1) | 2024.11.04 |