템플릿 메서드 패턴이란?

2024. 6. 19. 17:51카테고리 없음

템플릿 메서드는 상위 클래스에서 알고리즘의 구조를 정의하고,

하위 클래스들이 알고리즘의 특정 단계들을 오버라이딩​할 수 있도록 하는 디자인 패턴이다.

 

일단 예시를 보자

 

<커피를 만드는 레시피>와 <홍차를 만드는 레시피>가 있다.

커피를 만드는 레시피☕

1. 물을 끓인다.
2. 끓는 물에 커피를 우려낸다.
3. 커피를 컵에 따른다.
4. 설탕과 우유를 추가한다.

홍차를 만드는 레시피🍵

1. 물을 끓인다
2. 끓는 물에 찻잎을 우려낸다.
3. 홍차를 컵에 따른다.
4. 레몬을 추가한다.

한 눈에 봐도 두 레시피가 상당히 유사한 것을 알 수 있다.

 

 

 

 

코드를 통해 알아봅시다.

Coffee.java

public class Coffee {
	
    void prepareRecipe() {
    	boilWater();		// 1. 물을 끓인다.
        brewCoffeeGrinds();	// 2. 커피를 우려낸다.
        pourInCup();		// 3. 커피를 컵에 따른다.
        addSugarAndMilk();	// 4. 설탕과 우유를 추가한다.
    }
    
    public void boilWater() {
    	System.out.println("물 끓이는 중");
    }
    
    public void brewCoffeeGrinds() {
    	System.out.println("필터로 커피를 우려내는 중");
    }
    
    public void pourInCup() {
    	System.out.println("컵에 따르는 중");
    }
    
    public void addSugarAndMilk() {
    	System.out.println("설탕과 우유를 추가하는 중");
    }
}

 

 

Tea.java

public class Tea {
	
    void prepareRecipe() {
    	boilWater();	// 1. 물을 끓인다.
        steepTeaBag();	// 2. 찻잎을 우려낸다.
        pourInCup();	// 3. 홍차를 컵에 따른다.
        addLemon();	// 4. 레몬을 추가한다.
    }
    
    public void boilWater() {
    	System.out.println("물 끓이는 중");
    }
    
    public void steepTeaBag() {
    	System.out.println("찻잎을 우려내는 중");
    }
    
    public void pourInCup() {
    	System.out.println("컵에 따르는 중");
    }
    
    public void addLemon() {
    	System.out.println("레몬을 추가하는 중");
    }
}

 

커피와 홍차를 만드는 과정이 상당히 유사해 코드 중복이 많이 발생한다.

 

 

1번 물을 끓이는 과정과 3번 컵에 따르는 과정의 코드는 완벽히 일치하고,

2번과 4번 과정은 약간씩 다르지만, 비슷하다고 볼 수 있다.

 

2.

끓는 물에 커피를 우려낸다. --> brewCoffeeGrinds()

끓는 물에 찻잎을 우려낸다. --> steepTeaBag()

---> brew()라는 공통 메서드를 상위 클래스에 만들어서 사용할 수 있다.

 

4.

설탕과 우유를 추가 --> addSugarAndMilk()

레몬을 추가 --> addLemon()

---> addCondiments()라는 공통 메서드를 상위 클래스에 만들어서 사용할 수 있다.

 

 

 

 

이제 공통적인 부분을 찾아 일반화해서 상위 클래스를 만들어 봅시다

public abstract class CaffeineBeverage {
    final void prepareRecipe() {
    	boilWater();
        brew();
        pourInCup();
        addCondiments();
    }
    
    abstract void brew();
    
    abstract void addCondiments();
    
    void boilWater() {
    	System.out.println("물 끓이는 중");
    }
    
    void pourInCup() {
    	System.out.println("컵에 따르는 중");
    }
}

 

Coffee 클래스와 Tea 클래스에 있던 prepareRecipe() 라는 메서드를 템플릿(틀)으로 만들었고,

여기서는 카페인 음료를 만드는 알고리즘의 템플릿이 된다.

 

그리고 완전히 과정이 일치하는 boilWater()와 pourInCup()은 이 상위 클래스에서 구현하고 있다.

 

또한 음료마다 조금씩 달랐던 brew()와 addCondiments() 메서드는 하위 클래스에서 구현할 수 있도록 추상 메서드로 선언되었다.

 

 

---수정된  Coffee.java---

public class Coffee extends CaffeineBeverage {
    @Override
    public void brew() {
    	System.out.println("필터로 커피를 우려내는 중");
    }
    
    @Override
    public void addCondiments() {
    	System.out.println("설탕과 우유를 추가하는 중");
    }
}

 

---수정된 Tea.java---

public class Tea extends CaffeinBeverage {
    @Override
    public void brew() {
    	System.out.println("찻잎을 우려내는 중");
    }
    
    @Override
    public void addCondiments() {
    	System.out.println("레몬을 추가하는 중");
    }
}

 

brew()와 addCondiments()가 각각의 레시피에 맞게 오버라이딩이 되어있다.

 

 

 

이전 코드에서는 Coffee와 Tea가 각각 작업을 처리했어서 코드가 중복되고, 만약 레시피가 바뀐다면 일일이 음료 하나하나의 과정을 고쳐야 한다. 

또한 새로운 음료가 추가된다면 또 그 레시피를 전부 구현해야 하기 때문에, 변경과 확장에 어려운 상태가 된다.


하지만? 템플릿 메서드를 적용한다면 상위클래스(CaffeineBeverage)에서 알고리즘을 구성하고 작업을 처리하기 때문에 알고리즘이 한 군데에 모여있게 되어 수정에 용이하고 코드를 재사용할 수 있다.!!!

 

또한 새로운 음료가 추가되어도 이미 레시피 틀이 만들어져있기 때문에 쉽게 추가할 수 있다.

 

 

 

이렇게 템플릿 메서드는 알고리즘의 골격(틀)을 정의하고, 서브클래스에서 일부 단계를 구현할 수 있으며, 

알고리즘의 구조는 그대로 유지하면서 특정 단계는 서브 클래스에서 재정의할 수 있다.

 

 

 

AbstractClass에 알고리즘의 틀을 정의한 템플릿 메서드가 존재하고, 서브클래스가 알고리즘의 틀을 마음대로 수정하지 못하게 final로 선언할 수 있다.

 

primitiveOperation1()과 primitiveOperation2()는 추상메서드로, ConcreteClass에서 구체적인 구현을 한다.

또한 ConcreteClass는 여러 개가 있을 수 있다.

 

abstract class AbstractClass {
	
    final void templateMethod() {  // 템플릿 메서드는 각 단계를 메서드로 표현한다.
    	primitiveOperation1();
        primitiveOperation2();
        concreteOperation();
    }
    
    abstract void primitiveOperation1();  // 알고리즘의 이 단계는 서브클래스에서 구현하도록 한다.
    
    abstract void primitiveOperation2();  // 알고리즘의 이 단계는 서브클래스에서 구현하도록 한다2.
    
    void concreteOperation() {
    	// 구현 코드
    }
}

 

 

 

 

 

템플릿 메서드 패턴 장점

 

- 상위 추상클래스로 로직을 공통화 하여 코드의 중복을 줄일 있다.

 

- 서브 클래스의 역할을 줄이고, 핵심 로직을 상위 클래스에서 관리하므로서 관리가 용이해진다

 

 

 

템플릿 메서드 패턴 단점

 

- 알고리즘의 제공된 골격에 의해 유연성이 제한될 있다.

 

- 알고리즘 구조가 복잡할수록 템플릿 로직 형태를 유지하기 어려워진다.

 

- 추상 메소드가 많아지면서 클래스의 생성, 관리가 어려워질 있다.