템플릿 메소드 패턴 이란
언제 사용하는것이 좋은가 ?
변하는 부분과 변하지 않는 부분이 있고 변하지 않는 부분의 관리포인트가 많을 때 사용하면 좋다.
만약 커피와 차를 만든다고 가정해보자
커피를 끓일때와 차를 끓일때 방법이 비슷하다면?
커피
1. 물을 끓인다
2. 커피를 우려낸다.
3. 커피를 컵에 따른다.
4. 설탕과 우유를 추가한다.
차
1. 물을 끓인다
2. 차를 우려낸다.
3. 차를 컵에 따른다.
4. 레몬을 추가한다.
자 이렇게 되면 우리는 흔히
package template.first;
public abstract class Beverage {
abstract void prepareRecipe();
void boilWater(){
System.out.println("물을 끓인다");
}
void pourlnCup(){
System.out.println("컵에 따른다");
}
}
package template.first;
public class Coffee extends Beverage {
@Override
void prepareRecipe() {
boilWater();
brewCoffeGrinds();
pourlnCup();
addSugarandMilk();
}
void brewCoffeGrinds(){
System.out.println("커피를 따른다.");
}
void addSugarandMilk(){
System.out.println("설탕과 우유를 넣는다.");
}
}
package template.first;
public class Tee extends Beverage {
@Override
void prepareRecipe() {
boilWater();
brewTeeGrinds();
pourlnCup();
addLemon();
}
void brewTeeGrinds(){
System.out.println("차를 따른다.");
}
void addLemon(){
System.out.println("레몬을 넣는다.");
}
}
다음과 같은 코드를 구현할 것이라 생각한다.
하지만
방법을 보면 두가지의 알고리즘은 똑같다는 것을 알 수 있다.
1.3 은 이미 클래스에 추상화되어 있다.
하지만 2.4 는 추상화되지 않았지만 똑같다. 서로 다른 음료에 적용될 뿐이다.
그렇다면 prepareRecipe() 를 추상화시킬 방법을 찾아보자
차를 따른다 , 커피를 따른다
모두 따른다가 공통이다. 따라서 brew() 라고 지어놓고 모두 똑같은 이름의 메소드를 쓰게한다.
이와 마찬가지로
설탕을 추가한다와 레몬을 추가한다
모두 addCondiments 속성을 추가한다의 메소드로 양쪽을 사용해도 괜찮을 것 같다.
새로운 prepareRecipe() 메소드가 준비되었다. 이제 이 메소드를 코드에 집어넣어보자
package template.second;
public abstract class Beverage2 {
// 아무나 override 해서 사용하지 못하도록 final 로 선언해둔다.
final void prepareRecipe(){
boilWater();
brew();
pourlnCup();
addCondiments();
}
// 서로 다른곳에서 부를 것이기 때문에 추상메소드화 해놓는다.
abstract void addCondiments();
// 서로 다른곳에서 부를 것이기 때문에 추상메소드화 해놓는다.
abstract void brew();
void boilWater(){
System.out.println("물을 끓인다");
}
void pourlnCup(){
System.out.println("컵에 따른다");
}
}
package template.second;
public class Coffee extends Beverage2 {
@Override
void addCondiments() {
System.out.println("설탕과 우유를 추가한다.");
}
@Override
void brew() {
System.out.println("커피를 우려낸다");
}
}
package template.second;
public class Tee extends Beverage2 {
@Override
void addCondiments() {
System.out.println("레몬을 추가한다.");
}
@Override
void brew() {
System.out.println("차를 우려낸다.");
}
}
템플릿 메소드를 적용하면
우선 Beverage 클래스에서 알고리즘을 독점하게 된다.
덕분에 서브 클래스에서 코드를 재사용할 수 있다.
알고리즘이 한군데 모여있기 때문에 그부분만 고치면 된다.
템플릿메소드 패턴을 지키는 다른 음료를 손쉽게 추가할 수 있다.
일부 구현만 서브클래스에 의존하면 된다.
템플릿 메소드에 대해서 좀더 구체적으로 알아보자
package template.second;
public abstract class AbstractTemplateClass {
final void templateMethod(){
primitiveOperation1();
primitiveOperation2();
concreteOperation();
hook();
}
// 구현해서 쓰게하거나
abstract void primitiveOperation1();
abstract void primitiveOperation2();
final void concreteOperation(){
System.out.println("강제하거나");
}
// 오버라이드 해서 사용하게끔 만들어 놓거나
void hook(){}
}
package template.second;
public abstract class BeverageWithHook {
void prepareRecipe(){
boilWater();
brew();
pourInCup();
if(customerWantsCondiments()){
addCondiments();
}
}
abstract void addCondiments();
abstract void brew();
void boilWater(){
System.out.println("물 끓이는 중");
}
void pourInCup(){
System.out.println("컵에 따르는 중");
}
// 후크 오버라이드 해서 사용할 수 있음
boolean customerWantsCondiments(){
return true;
}
}
전략 패턴의 적용
개방 폐쇄 원칙을 잘 지키는 구조이면서도 템플릿 메소드 패턴보다 유연하고 확장성이 뛰어난 것이 , 오브젝트를 아예 둘로 분리하고 클래스 레벨에서는 인터페이스를 통해서만 의존하도록 만드는 전략 패턴이다.
전략 패턴은 OCP 관점에 보면 확장에 해당하는 변하는 부분을 별도의 클래스로 만들어 추상화된 인터페이스를 통해 위임하는 방식이다.
변하는 부분중에서 전략을 만들어 해당과정을 인터페이스로 만들고 그 구현체를 주입하는 방식으로 사용하는게 중요 요점이다.
'개발 > java' 카테고리의 다른 글
spring 에서 pageable custom 구현 (0) | 2019.12.19 |
---|---|
java 복잡한 Comparator 예제 (0) | 2019.12.19 |
어댑터 패턴 (0) | 2019.07.01 |
커맨드 패턴 (0) | 2019.06.30 |
자바 날짜계산 유틸리티 구현 (0) | 2019.02.19 |
댓글