정의
- 상위 class에서 처리의 흐름을 제어하고, 하위class에서 처리의 내용을 구체화 한다.
- 여러 class에서 공통되는 사항은 상위 추상 class에서 구현하고, 각각의 세부내용은 하위 class에서 구현한다.
- 그 결과 코드의 중복을 줄이고, Refactoring에 유리한 패턴으로 상속을 통한 확장 개발 방법으로 Strategy Pattern과 함께 많이 사용되는 pattern이다.
Hook method
- 필수적이지 않은 부분으로 필요에 따라 선택적으로 sub-class에서 구현할 경우 사용한다.
- Abstract class에서 정의된 어떠한 행도 하지 않을 수 있고, sub-class의 공통된 동작을 할 수도 있다.
- 또한, sub-class에서 hook method를 재정의 하여 사용하는 방법도 있다.
- Sub-class에 따라 algorithm에 원하는 내용을 추가할 수 있도록 하는 것이 hook-method의 존재 이유다.
요구사항
- 멤버 함수들의 접근 범위 지정을 명확히 한다.
- 가상함수, 일반함수로 선언 하는 것 에 대한 기준이 필요
- Virtual 함수의 수를 최소화한다.
Hollywood Principle
- Sub-class는 upper-class에 구성요소로써 활용될 수 있지만, sub-class가 upper-class를 호출하여 자신을 호출하게 하면 안된다는 것이다.
- Template Method Pattern에서도 이러한 원칙을 따르고 있는데,
설명을 더하면, abstract class에서는 sub-class에 있는 기능을 호출할 수 있지만,
sub-class에서는 abstract-class의 template method를 호출하거나 수정할 수 없다는 것이다.
장점
- 상위위 class의 template method에서 알고리즘이 기술되어 있으므로,
- 하위 class에서는 알고리즘을 일일이 기술할 필요가 없다.
소스코드
public abstract class CaffeineBeverageWithHook {
void prepareRecipe(){
boilWater();
brew();
pourInCup();
if(customerWantsCondiments()){
addCondiments();
}
}
abstract void brew();
abstract void addCondiments();
void boilWater(){
System.out.println("Boiling water");
}
void pourInCup(){
System.out.println("Pouring into cup");
}
boolean customerWantsCondiments(){
return true;
}
}
public class CoffeeWithHook extends CaffeineBeverageWithHook{
@Override
void brew() {
System.out.println("Dripping Coffee through filter");
}
@Override
void addCondiments() {
System.out.println("Adding Sugar and Milk");
}
@Override
public boolean customerWantsCondiments() {
String answer = getUserInput();
if(answer.toLowerCase().startsWith("y")){
return true;
}else{
return false;
}
}
private String getUserInput(){
String answer = null;
System.out.println("would you like milk&sugar with your coffee (y/n) ?");
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
try {
answer = in.readLine();
} catch (Exception e) {
System.err.println("IO error trying to read your answer");
}
if(answer==null){
return "no";
}
return answer;
}
}
public class TeaWithHook extends CaffeineBeverageWithHook{
@Override
void brew() {
System.out.println("Steeping the tea");
}
@Override
void addCondiments() {
System.out.println("Adding Lemon");
}
@Override
public boolean customerWantsCondiments() {
String answer = getUserInput();
if(answer.toLowerCase().startsWith("y")){
return true;
}else{
return false;
}
}
private String getUserInput(){
String answer = null;
System.out.println("would you like lemon with your tea (y/n) ?");
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
try {
answer = in.readLine();
} catch (Exception e) {
System.err.println("IO error trying to read your answer");
}
if(answer==null){
return "no";
}
return answer;
}
}
public class BeverageTestDrive {
public static void main(String[] args) {
TeaWithHook teaWithHook = new TeaWithHook();
CoffeeWithHook coffeeWithHook = new CoffeeWithHook();
System.out.println("\n Making tea");
teaWithHook.prepareRecipe();
System.out.println("\nMaking coffee");
coffeeWithHook.prepareRecipe();
}
}
결과
Making tea
Boiling water
Steeping the tea
Pouring into cup
would you like lemon with your tea (y/n) ?
y
Adding Lemon
Making coffee
Boiling water
Dripping Coffee through filter
Pouring into cup
would you like milk &sugar with your coffee (y/n) ?
y
Adding Sugar and Milk
UML