이론/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