Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 순열
- 코딩테스트
- 실업급여
- 청년내일채움공제
- 사회초년생
- 네트워크
- leetcode
- IT기초
- 자료구조
- 캡쳐링
- 전화영어
- 취업사실신고
- 회사폐업
- 모여봐요동물의숲
- HeadFirstDesignPatterns
- 부분합알고리즘
- 동적계획법
- 후니의쉽게쓴시스코라우팅
- 후니의쉽게쓴시스코네트워킹
- 정보처리기사개정
- 프로그래머스
- C++
- 정보
- 생애첫계약
- 튜터링
- array
- 실업인정인터넷신청
- 알고리즘
- 자취준비
- 막대기자르기
Archives
- Today
- Total
따봉도치야 고마워
Head First Design Patterns : (8)템플릿 메소드 패턴 본문
템플릿 메소드 패턴
- 메소드에서 알고리즘의 골격(틀)을 정의하고, 여러 단계 중 일부는 서브 클래스에서 구현할 수 있다.
- 템플릿 메소드를 이용하면 알고리즘의 구조는 그대로 유지하면서 서브클래스에서 특정 단계를 재정의 할 수 있다.
예제
- 커피와 차를 만드는 클래스를 추상화 해보자
Coffee | Tea |
1. 물을 끓인다. 2. 끓는 물에 커피를 우려낸다. 3. 커피를 컵에 따른다. 4. 설탕과 우유를 추가한다. |
1. 물을 끓인다. 2. 끓는 물에 차를 우려낸다. 3. 차를 컵에 따른다. 4. 레몬을 추가한다. |
- 기존 커피/차 클래스
public class Coffee{
void prepareRecipe(){
boilWater();
brewCoffeeGrinds();
pourInCup();
addSugarAndMilk();
}
public void boidWater(){ System.out.println("물 끓이는 중"); }
public void brewCoffeeGrinds(){ System.out.println("커피 우려내는 중"); }
public void pourInCup(){ System.out.println("컵에 따르는 중"); }
public void addSugarAndMilk(){ System.out.println("설탕과 우유를 추가하는 중"); }
}
public class Tea{
void prepareRecipe(){
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}
public void boidWater(){ System.out.println("물 끓이는 중"); }
public void steepTeaBag(){ System.out.println("차를 우려내는 중"); }
public void pourInCup(){ System.out.println("컵에 따르는 중"); }
public void addLemon(){ System.out.println("레몬을 추가하는 중"); }
}
- 부모에 prepareRecipe()를 추상메소드로 선언하고, 1번(boilWater)과 3번(pourInCup)을 정의한다 ?
- 더 추상화 할 순 없을까?
- 2, 4번도 '우려낸다' 와 '첨가물을 추가한다'로 추상화가 가능함.
- 메소드 명을 일치시키면 prepareRecipe()메소드도 부모클래스에서 정의 가능.
public abstract class CaffeineBeverage{
//음료 만드는 과정을 변경할 수 없게 final로 선언
final void prepareRecipe(){
boilWater();
brew();
pourInCup();
addCondiments();
}
//커피와 차가 다른 방식으로 처리하기 때문에 추상 메소드로 선언 (서브클래스에서 정의)
abstract void brew();
abstract void addCondiments();
//커피와 차 둘 다 동일한 부분이기 때문에 여기서 정의
void boidWater(){ System.out.println("물 끓이는 중"); }
void pourInCup(){ System.out.println("컵에 따르는 중"); }
}
- 커피와 차 클래스에선 위의 클래스를 상속받아 brew(), addCondiments() 메소드만 정의해주면 됨.
- 여기서 prepareRecipe()가 템플릿 메소드 : 어떤 알고리즘에 대한 템플릿(틀) 역할을 함
개선점
- CaffeineBeverage에서 알고리즘을 독점해, 알고리즘에 대한 지식과 구현 방법이 분산되지 않음.
- 일부 구현만 서브 클래스에 의존
- 서브 클래스에서 코드를 재사용할 수 있음
- 음료 추가 및 수정 시에도 비교적 간단함
템플릿 메소드와 후크(hook)
후크(hook) : 추상클래스에서 선언되는 메소드지만 기본적인 내용만 구현 or 아무 코드도 들어있지 않은 메소드
서브클래스 입장에선 다양한 위치에서 알고리즘에 끼어들 수 있음
- 알고리즘에서 필수적이지 않은 부분
- 템플릿 메소드에서 앞으로 일어날 일, 막 일어난 일에 대해 서브클래스에서 반응할 기회를 제공하기 위한 용도
- 진행되는 작업에 대한 결정을 내리는 기능
ex.
final void prepareRecipe(){
boilWater();
brew();
pourInCup();
if(customerWantsCondiments()){
addCondiments();
}
}
boolean customerWantsCondiments(){
return true;
}
- customerWantsCondiments()가 후크
- 서브클래스에서 필요에 따라 내용을 변경해 알고리즘에 영향을 끼칠 수 있음
디자인 원칙
- 헐리우드 원칙 - "먼저 연락하지 마세요. 저희가 연락드리겠습니다"
- 의존성 부패(dependency rot, 의존성이 복잡하게 꼬여있은 것)를 방지 할 수 있음.
- 해당 원칙을 사용하면 저수준 구성요소에서 시스템에 접속할 수 있지만, 언제 어떤식으로 구성요소를 사용할지는 고수준 구성요소에서 결정하게 됨.
- 즉 고수준 구성요소에서 저수준에게 하는 말이라고 생각
Q&A
1. 템플릿을 만들 때 언제 추상메소드를 사용하고 언제 후크를 사용하는지
- 서브클래스에서 알고리즘의 특정 단계를 제공해야할 때 추상 메소드
- 알고리즘의 특정 부분이 선택적으로 적용된다든가 하는 경우에 후크 - 후크는 선택적 구현이 가능하니까
2. 추상 메소드가 너무 많아지면 안 좋을 것 같은데
- 맞음. 따라서 알고리즘의 단계를 적절히 나누고, 필수적이지 않은 부분은 후크로 구현하면 부담이 줄음
3. 의존성 뒤집기 원칙과 헐리우드 원칙
둘 다 객체를 분리시킨다는 하나의 목표를 공유하지만
- 의존성 뒤집기는 구상클래스 사용을 줄이고, 추상화된 것을 사용한다는 원칙 -> 의존성을 피하는데 있어 더 강하고 일반적
- 헐리우드 원칙은 저수준 구성요소와 고수준 계층 사이에 의존성이 없는 프레임워크를 구축해줌
+
자바 Arrays.Sort()
- sort() 내부에서 부르는 MergeSort()가 템플릿 메소드
- MergeSort()에선 Arrays 클래스에 정의된 구상 메소드인 swap()과 서브클래스에서 구현해야하는 compareTo()를 사용
- Arrays의 서브 클래스를 만들 수 없는데 어떻게 하지?
- sort()를 정적 메소드로 만들어 모든 배열에서 사용 가능
- but, 각 요소에 compareTo() 메소드가 구현 되어 있는지 알아낼 방법이 필요
- -> Comparable 인터페이스 구현해 compareTo() 메소드 구현
Q. 이것도 템플릿 메소드라고 할 수 있는지 (상속을 사용하지 않는데)
- 실전에서 패턴을 적용하는 방법이 교재에 나와있는 방법과 완전히 같을 순 없음
- 게다가 어떤 배열에서도 정렬 기능을 사용할 수 있어야하기 때문에 정적메소드를 정의하고, 각 객체가 정렬에 대해 구현하도록 해 오히려 더 유연해짐
Q. 스트래티지 패턴과 더 가까워 보이는데?
- 스트래티지 패턴에선 구성을 위해 사용하는 클래스에서 알고리즘을 완전히 구현해야 되는데 MergeSort()는 불완전(compareTo)
+ 스윙 JFrame의 update() : paint()라는 후크 메소드를 호출함
+ Applet : init(), start(), stop(), destroy(), paint() 각 시점에 실행되는 후크 메소드들이 있음
템플릿 메소드 패턴 vs 스트래티지 패턴
템플릿 메소드 패턴 | 스트래티지 패턴 | |
특징 | 알고리즘의 개요를 정의 서브클래스에서 일부 작업을 구현 상속 사용 |
알고리즘 군을 캡슐화해 그것들을 서로 바꿔가며 사용 가능 |
장단점 | 프레임워크를 만들기 좋음 각 단계별 다른 구현을 사용하면서도 구조는 유지됨 코드 중복이 적음 |
구성을 사용해 더 유연하고 의존성이 적음 |
+ 팩토리 메소드 패턴은 특화된 템플릿 메소드 패턴이라고 불림
'프로그래밍 > 공부' 카테고리의 다른 글
Head First Design Patterns : (10)스테이트 패턴 (0) | 2020.09.30 |
---|---|
Head First Design Patterns : (9)이터레이터와 컴포지트 패턴 (0) | 2020.09.24 |
Head First Design Patterns : (7)어댑터 패턴과 퍼사드 패턴 (0) | 2020.09.21 |
Head First Design Patterns : (6)커맨드 패턴 (0) | 2020.09.17 |
Head First Design Patterns : (5)싱글턴 패턴 (0) | 2020.09.15 |
Comments