이론/GoF
[행동패턴] 템플릿메소드 패턴
혀끄니
2023. 8. 24. 09:00
728x90
- 템플릿메소드(Template method) 패턴
- 알고리즘 구조를 서브 클래스가 확장할 수 있도록 템플릿으로 제공하는 방법
- 추상클래스는 템플릿을 제공하고 하위 클래스는 구체적인 알고리즘을 제공한다.
탬플릿메소드 패턴 적용전
number.txt
1
2
3
4
5
client
public class Client {
public static void main(String[] args) {
FileProcessor fileProcessor = new FileProcessor("number.txt");
int result = fileProcessor.process();
System.out.println(result);
}
}
public class FileProcessor {
private String path;
public FileProcessor(String path) {
this.path = path;
}
public int process() {
try(BufferedReader reader = new BufferedReader(new FileReader(path))) {
int result = 0;
String line = null;
while((line = reader.readLine()) != null) {
result += Integer.parseInt(line);
}
return result;
} catch (IOException e) {
throw new IllegalArgumentException(path + "에 해당하는 파일이 없습니다.", e);
}
}
}
public class MultuplyFileProcessor {
private String path;
public MultuplyFileProcessor(String path) {
this.path = path;
}
public int process() {
try(BufferedReader reader = new BufferedReader(new FileReader(path))) {
int result = 0;
String line = null;
while((line = reader.readLine()) != null) {
result *= Integer.parseInt(line);
}
return result;
} catch (IOException e) {
throw new IllegalArgumentException(path + "에 해당하는 파일이 없습니다.", e);
}
}
}
문제점
- 같은 구문이 계속 반복되서 사용하는 문제점 발생
템플릿메소드 패턴 적용후
상속을 사용하는 방식
public abstract class FileProcessor {
private String path;
public FileProcessor(String path) {
this.path = path;
}
public final int process(Operator operator) {
try(BufferedReader reader = new BufferedReader(new FileReader(path))) {
int result = 0;
String line = null;
while((line = reader.readLine()) != null) {
result = getResult(result, Integer.parseInt(line));
}
return result;
} catch (IOException e) {
throw new IllegalArgumentException(path + "에 해당하는 파일이 없습니다.", e);
}
}
protected abstract int getResult(int result, String line);
}
public class Plus implements FileProcessor {
public Plus(String path){
super(this);
}
@Override
public int getResult(int result, int number) {
return result += number;
}
}
public class Multiply implements FileProcessor {
public Multiply (String path){
super(this);
}
@Override
public int getResult(int result, int number) {
return result *= number;
}
}
public class Client {
public static void main(String[] args) {
FileProcessor fileProcessor = new Multiply("number.txt");
int result = fileProcessor.process();
System.out.println(result);
}
}
템플릿 콜백(Template-Callback) 패턴
- 콜백으로 상속 대신 위임을 사용하는 템플릿 패턴
- 상속 대신 익명 내부 클래스 또는 람다 표현식을 활용할 수 있다.
- 콜백으로 할 경우 하나의 메소드만 가지고 있어야 한다.
public interface Operator {
abstract int getResult(int result, int number);
}
//abstract가 사라짐
public class FileProcessor {
private String path;
public FileProcessor(String path) {
this.path = path;
}
public final int process(Operator operator) {
try(BufferedReader reader = new BufferedReader(new FileReader(path))) {
int result = 0;
String line = null;
while((line = reader.readLine()) != null) {
// operator를 호출
result = operator.getResult(result, Integer.parseInt(line));
}
return result;
} catch (IOException e) {
throw new IllegalArgumentException(path + "에 해당하는 파일이 없습니다.", e);
}
}
// 추상메소드가 필요없어짐
// protected abstract int getResult(int result, int number);
}
public class Client {
public static void main(String[] args) {
FileProcessor fileProcessor = new Multiply("number.txt");
int result = fileProcessor.process((sum, number) -> sum += number);
System.out.println(result);
}
}
public class Client {
public static void main(String[] args) {
FileProcessor fileProcessor = new FileProcessor("number.txt");
int result = fileProcessor.process((result, number) -> result += number);
System.out.println(result);
}
}
템플릿메소드와 템플릿 콜백메소드와의 차이점
- Multifly와 Plus 클래스가 사라지고 Client 코드에서 익명클래스로 대신한다.
- 하지만 익명클래스가 싫다면 그냥 Multifly와 Plus 클래스를 만들어서 사용해도 된다.
장점
- 템플릿 코드를 재사용하고 중복 코드를 줄일 수 있다.
- 템플릿 코드를 변경하지 않고 상속을 받아서 구체적인 알고리즘만 변경할 수 있다.
단점
- 리스코프 치환 원칙을 위반할 수 있다.
- 알고리즘 구조가 복잡할 수 록 템플릿을 유지하기 어려워진다.
실무사용예
Java
- HttpServlet
스프링
1. 템플릿 메소드 패턴
- Configuration
2. 템플릿 콜백 패턴
- JdbcTemplate
- RestTemplate
728x90