Template Method

GamSa Ham·2022년 11월 15일
0

GoF디자인패턴

목록 보기
17/22

의도

  • 객체의 연산에 알고리즘의 뼈대만 정의하고 각 단계에서 수행하는 구체적 처리는 서브클래스에서 하도록 함
  • 알고리즘 구조 자체는 그대로 둔 채 각 단계 처리를 서브클래스에서 재정의 할 수 있게 함

동기

  • 서브클래스가 오버라이드 할 수 있는 추상 연산을 사용하여 알고리즘을 정의
  • 추상 연산을 통해서 알고리즘의 일부 단계를 정의함으로써 각 단계의 순서는 고정하되 서브클래스를 사용하여 필요에 따라 단계의 처리를 다양화

⇒ 1. 알고리즘을 여러 단계로 나눔

  1. 나눠진 알고리즘의 단계를 메소드로 선언
  2. 알고리즘을 수행할 템플릿 메소드를 만듦
  3. 하위 클래스에서 나눠진 메소드들을 구현

구조

참여자

  • Abstract Class : 서브클래스들이 재정의를 통해 구현해야 하는 알고리즘 처리 단계 내의 기본 연산을 정의 알고리즘의 뼈대를 정의하는 템플릿 메서드 구현함
  • Concrete Class : 서브클래스마다 달라진 알고리즘 처리 단계를 수행하기 위한 기본 연산 구현

예제

package templateMethod;

public abstract class Ramen {

	public final void makeRamen() {
		boilWater();
		putNoodles();
		putPeppers();
		putExtra();
		waitForMinutes();
	}
	
	protected void boilWater() {
		System.out.println("물을 끓인다.");
	}
	
	protected void putNoodles() {
		System.out.println("면을 넣는다.");
	}
	
	protected abstract void putPeppers();
	
	protected void putExtra() {}
	
	protected abstract void waitForMinutes();
	
}

package templateMethod;

public class OrgRamen extends Ramen {

	@Override
	protected void putPeppers() {
		System.out.println("일반 고추가루를 넣는다");
	}

	@Override
	protected void waitForMinutes() {
		System.out.println("3분간 기다린다.");
	}
	
}

package templateMethod;

public class spicyRamen extends Ramen {

	@Override
	protected void putPeppers() {
		System.out.println("청양고추가루를 넣는다.");
	}

	@Override
	protected void waitForMinutes() {
		System.out.println("4분간 기다린다.");
	}

}

package templateMethod;

public class RamenWithEggs extends Ramen{

	@Override
	protected void putPeppers() {
		System.out.println("일반 고춧가루를 넣는다.");
	}

	@Override
	protected void waitForMinutes() {
		System.out.println("5분간 기다린다.");
	}
	
	@Override
	protected void putExtra() {
		System.out.println("계란을 넣는다.");
	}

}

package templateMethod;

public class Main {
	
	public static void main(String[] args) {
		Ramen orgRamen = new OrgRamen();
		Ramen spicyRamen = new spicyRamen();
		Ramen eggRamen = new RamenWithEggs();
		
		
		orgRamen.makeRamen();
		System.out.println("===============");
		spicyRamen.makeRamen();
		System.out.println("===============");
		eggRamen.makeRamen();
	}

}

물을 끓인다.
면을 넣는다.
일반 고추가루를 넣는다
3분간 기다린다.
===============
물을 끓인다.
면을 넣는다.
청양고추가루를 넣는다.
4분간 기다린다.
===============
물을 끓인다.
면을 넣는다.
일반 고춧가루를 넣는다.
계란을 넣는다.
5분간 기다린다.

활용성

  • 어떤 알고리즘을 이루는 부분 중 변하지 않는 부분을 정의하고 다양해질 수 있는 부분은 서브클래스에서 정의할 수 있도록 남겨두고자 할 때
  • 서브클래스 사이의 공통적인 행동을 추출해 하나의 공통 클래스에 몰아둠으로써 코드 중복을 피하고 싶을 때

⇒ 기존 코드에서 나타나는 차이점을 뽑아 이를 별도의 새로운 연산들로 구분 후 달라진 코드 부분을 이 새로운 연산을 호출하는 템플릿 메서드로 대체

  • 서브클래스의 확장을 제어할 수 있음

⇒ 템플릿 메서드가 어떤 특정한 시점에 훅(hook) 연산을 호출하도록 정의함으로써 특정 시점에서만 확장되도록 함

협력 방법

  • ConcreteClass는 AbstractClass를 통하여 알고리즘의 변하지 않는 처리 단계 구현

결과

  • 템플릿 메서드는 코드 재사용을 위한 기본 기술
  • 클래스 라이브러리 구현 시 중요한 기술

⇒ 라이브러리에 정의할 클래스들의 공통 부분을 분리하는 수단

  • 부모 클래스는 서브클래스에 정의된 연산을 호출할 수 있지만 반대 방향의 호출은 안됨
  • 템플릿 메서드에서 호출 가능한 연산

⇒ 구체 연산(concrete operation) : ConcreteClass나 사용자 클래스에 정의된 연산

AbstractClass 구체 연산: 서브클래스에서 일반적으로 유용한 연산

기본 연산 : 추상화된 연산

팩토리 메서드

훅 연산 : 필요하다면 서브클래스에서 확장할 수 있는 기본 행동을 제공하는 연산으로 기본적으로 아무 내용도 정의하지 않음

  • 어떤 연산이 훅 연산(오버라이드 가능)인지 추상 연산(오버라이드 필수)인지 지정하는 것이 매우 중요

구현

  • 호출하는 기본 연산들을 protected 멤버로 구현하여 템플릿 메서드만 호출할 수 있게 함
  • 템플릿 메서드 자체는 재정의되면 안됨
  • 기본 연산의 수를 최소화 해야함
  • 이름을 짓는 규칙을 만듦

추가

  • 팩토리 메서드와 차이 : 해결하려는 목적에 차이점이 있음 팩토리 메서드(생성 패턴) 객체의 생성과 관련 템플릿 메서드(행동 패턴) 어떤 태스크, 알고리즘을 어떤 객체에 할당하는 것이 좋을지 정의
profile
안녕하세요. 자바를 좋아하고 디자인 패턴, Refactoring, Clean Code에 관심이 많은 백엔드 개발자입니다.

0개의 댓글