티스토리 뷰

디자인 패턴

22. 템플릿 메소드 패턴

jin-park 2022. 2. 11. 11:41

1. 템플릿 메소드 패턴

  • 알고리즘 구조를 서브 클래스가 확장할  있도록 템플릿으로 제공하는 방법
    • 추상 클래스는 템플릿을 제공하고, 하위 클래스는 구체적인 알고리즘을 제공한다.

다이어그램

  • AbstractClass : 템플릿 메서드를 정의하는 클래스 , 하위클래스에 공통 알고리즘을 정의하고 하위 클래스에 구체적인 알고리즘을 정의하는 클래스
    • templateMethod() : 알고리즘의 구조를 표현한 메소드 (데이터를 읽고,처리하고, 리턴해주는)
    • step1() : 하위클래스의 구체적인 알고리즘1
    • step2() : 하위클래스의 구체적인 알고리즘2
  • ConcreateClass :  물려받은 templateMethod를 구현하는 클래스
    • step1() : 재정의가 필요한 하위클래스의 구체적인 알고리즘1
    • step2() : 재정의가 필요한 하위클래스의 구체적인 알고리즘2

2. 템플릿 메소드 패턴 적용

  • 숫자로 구성된 파일을 읽어 덧셈과 곱셈을 하는 코드를 작성한다.

2.1. 적용 전 코드

  • 처리하는 코드의 중복이 많이 발생
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 class Client {
    public static void main(String[] args) {
        FileProcessor fileProcessor = new FileProcessor("number.txt");
        int result = fileProcessor.process();
        System.out.println(result);
    }
}

2.2. 적용 후 코드1 : 상속을 이용한 템플릿메소드 패턴 적용

public abstract class FileProcessor {
 
    private String path;
    public FileProcessor(String path) {
        this.path = path;
    }
 
    public final int process() {
        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)); //해당 Line을 구체적인 알고리즘의 메소드화 진행
            }
            return result;
        } catch (IOException e) {
            throw new IllegalArgumentException(path + "에 해당하는 파일이 없습니다.", e);
        }
    }
 
    protected abstract int getResult(int result, int number);
 
}
public class Plus extends FileProcessor {
    public Plus(String path){
       super(path);
    }
 
    @Override
    public int getResult(int result, int number) {
        return result += number;
    }
}
public class Multiply extends FileProcessor {
    public Multiply(String path) {
        super(path);
    }
 
    @Override
    protected 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((sum, number) -> sum += number);
        System.out.println(result);
    }
}

적용 후 코드2 : 템플릿 콜백(Template-Callback)패턴

  • 콜백으로 상속 대신 위임을 사용하는 템플릿 패턴 
    • 상속 대신 익명 내부 클래스 또는 람다 표현식을 활용할 수 있다.
  • 템플릿메소드패턴보다 클래스를 줄일 수 있다.
  • 전략 패턴의 기본 구조에 인터페이스를 상속하는 클래스를 만들지 않고 익명 내부 클래스를 활용하는 방식

다이어그램

public interface Operator {
    int getResult(int result, int number);
}
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) {
                result = operator.getResult(result, Integer.parseInt(line));
            }
            return result;
        } catch (IOException e) {
            throw new IllegalArgumentException(path + "에 해당하는 파일이 없습니다.", e);
        }
    }
}
public class Client {
    public static void main(String[] args) {
        // 람다식
        int result = fileProcessor.process((sum, number) -> sum += number);
        
        System.out.println(result);
        
        // 익명 내부 클래스
        int result2 = fileProcessor.process(new Operator(){
            @Override
            public int getResult(int result, int number){
            return result *= number;
            }
        });
        
        System.out.println(result2);
    }
}

3. 장점 및 단점

3.1. 장점

  • 템플릿 코드를 재사용하고 중복 코드를 줄일 수 있다.
  • 템플릿 코드를 변경하지 않고 상속을 받아서 구체적인 알고리즘만 변경할 수 있다.

3.2. 단점

  • 리스코프 치환 원칙을 위반할 수도 있다.
    • 리스코프 치환 원칙 : 상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다.
  • 알고리즘 구조가 복잡할 수록 템플릿을 유지하기 어려워진다.

위글은 인프런의 코딩으로 학습하는 GoF의 디자인 패턴강의를 정리하였습니다.

백기선님의 수락으로 정리하였으며 더 자세한 내용은 강의를 수강하시기 바랍니다.

https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4/dashboard

 

코딩으로 학습하는 GoF의 디자인 패턴 - 인프런 | 강의

디자인 패턴을 알고 있다면 스프링 뿐 아니라 여러 다양한 기술 및 프로그래밍 언어도 보다 쉽게 학습할 수 있습니다. 또한, 보다 유연하고 재사용성이 뛰어난 객체 지향 소프트웨어를 개발할

www.inflearn.com

 

'디자인 패턴' 카테고리의 다른 글

21. 전략 패턴  (0) 2022.02.11
20. 상태(State) 패턴  (0) 2022.02.11
19. 옵저버 패턴  (0) 2022.02.11
18. 메멘토 패턴  (0) 2022.02.10
17. 중재자 패턴  (0) 2022.02.10
댓글