부모 객체에는 알고리즘의 뼈대만을 정의하고 각 단계에서 수행할 구체적 처리는 서브클래스 쪽으로 미루는 패턴. 알고리즘의 구조 자체는 그대로 놔둔 채 알고리즘 각 단계의 처리를 서브클래스에서 재정의 할 수 있도록 하는 패턴.
커피와 홍차를 만든다고 생각하자. 커피와 홍차는 거의 비슷한 과정을 거쳐서 만들어지게 된다.
커피를 만드는 법
홍차를 만드는 법
우리는 이 과정들을 클래스로 만들어서 나타낼 수 나타낼 수 있다.
커피 만드는 클래스
public class Coffee {
void prepareRecipe() {
boilWater();
brewCoffeeGrinds();
pourInCup();
addSugarAndMilk();
}
public void boilWater() {
}
public void brewCoffeeGrinds() {
}
public void pourInCup() {
}
public void addSugarAndMilk() {
}
}
홍차를 우려내는 클래스
public class Tea {
void prepareRecipe() {
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}
public void boilWater() {
}
public void steepTeaBag() {
}
public void pourInCup() {
}
public void addLemon() {
}
}
이 두가지 클래스는 중복된 점이 많다. 만약 실제 클래스가 이렇게 제작되게 된다면 클래스의 변경이 일어나게 될 시 비슷한 두 클래스의 동작을 변경하기 위해서 두 클래스를 변경해야 하고 클래스를 각각 변경해야 한다.
그런데 이 두 클래스가 하는 일인 커피, 홍차의 제조를 생각하면 두 제조법의 알고리즘이 같다는 것을 알 수 있다.
제조법
우리는 이에 따라 만들어진 클래스들도 추상화 작업을 통해서 클래스의 중복된 코드와 알고리즘을 줄이고 공통화 시킬수 있다.
일단 우리는 레시피를 적용하는 함수를 보자
void prepareRecipe() {
boilWater();
brewCoffeeGrinds();
pourInCup();
addSugarAndMilk();
}
void prepareRecipe() {
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}
우리는 첫번째로 brewCoffeeGrinds
(커피를 필터로 우려내는 일)와 steepTeaBag
(티백을 물에 넣어서 홍차를 우려내는 일)이 거의 같다는 것을 안다. 그럼 우리는 두 함수를 brew
라는 함수로 공통화 시킬 수 있다.
또한 설탕과 우유를 추가하는 일과 레몬을 추가하는 일도 그와 같다는 것을 알 수 있다. 이 두가지 행위를 addCondiments
라는 함수로 공통화 시킬 수 있다.
void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}
이 함수를 사용하는 슈퍼 클래스를 만들어서 적용해보자
public abstract class Beverage {
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}
abstract void brew();
abstract void addCondiments();
void boilWater() {
System.out.println("물 끓이는 중");
}
void pourInCup() {
System.out.println("컵에 따르는 중");
}
}
그리고 커피와 홍차 음료 만드는 방법 두 클래스를 보자
Coffee 클래스
public class Coffee extends Beverage {
public void brew() {
System.out.println("필터로 커피를 우려내는 중");
}
public void addCondiments() {
System.out.println("설탕과 우유를 추가하는 중");
}
}
홍차 클래스
public class Tea extends Beverage {
public void brew() {
System.out.println("찻잎을 우려내는 중");
}
public void addCondiments() {
System.out.println("레몬을 추가하는 중");
}
}
이렇게 클래스의 알고리즘적으로 공통된 과정을 부모 클래스에서 정의하고 자식 클래스에서 세부적인 알고리즘 처리를 함으로 인해서 각 클래스마다 독립성에 대한 보장과 중복된 코드를 줄일 수 있다.
스프링의 dispatcher servlet
스프링의 DispatcherServlet은 하위 알고리즘 구현을 controller 에게 맡기고 자신은 공통적인 처리인 view 처리와 url 맵핑을 처리해서 스프링 프레임워크 사용자에게 편리성을 제공하는 방식으로 사용 되고 있다.