[JDK에서 찾아본 디자인 패턴] 3. 템플릿 메소드(Template method) 패턴

1. 템플릿 메소드(Template method)란?

알고리즘의 구조를 메소드에 정의하고, 하위 클래스에서 알고리즘 구조의 변경없이 알고리즘을 재정의 하는 패턴이다. 알고리즘이 단계별로 나누어 지거나, 같은 역할을 하는 메소드이지만 여러곳에서 다른형태로 사용이 필요한 경우 유용한 패턴이다.

템플릿 메소드의 정의만 읽는다면 무슨말인지 이해하기 어렵습니다. 아래 내용을 보면 생각보다 쉬운 패턴이란것을 알 수 있습니다.

<간단한 예제>

위 UML을 확인해보면 Coffee classTea classCaffeinBeverage abstract class를 상속받고 있습니다.
템플릿 메소드의 정의가 동일한 알고리즘의 구조를 하위 클래스가 필요에 따라 형태를 다르게하여 사용하는 패턴입니다.

아래 소스코드를 확인해보면 CaffeinBeverage abstract class에 존재하는 prepareReceipe()함수는 템플릿 메소드의 알고리즘 구조를 담당하고 있으며, brew()addCondiments()함수는 하위 클래스에서 재정의하여 각 클래스에 맞게 변형하여 사용합니다.

  • CaffeinBeverage.java
public abstract class CaffeinBeverage {
	final void prepareReceipe() {
		boilWater();
		brew();
		pourInCup();
		addCondiments();
	}

	abstract void addCondiments();
	abstract void brew();

	private void pourInCup() {
		System.out.println("컵에 따르는 중");
	}

	private void boilWater() {
		System.out.println("물 끓이는 중");
	}

}
  • Coffee.java
public class Coffee extends CaffeinBeverage {
	@Override
	void addCondiments() {
		System.out.println("설탕과 우유를 추가하는 중");
	}

	@Override
	void brew() {
		System.out.println("필터를 통해서 커피를 우려내는 중");
	}
}
  • Tea.java
public class Tea extends CaffeinBeverage {
	@Override
	void addCondiments() {
		System.out.println("레몬를 추가하는 중");
	}

	@Override
	void brew() {
		System.out.println("차를 우려내는 중");
	}
}
  • main.java
public static void main(String[] args) {
	//커피 만들기
	new Coffee().prepareReceipe();
	//차 만들기
	new Tea().prepareReceipe();
    
        //coffee output
        물 끓이는 중
        필터를 통해서 커피를 우려내는 중
        컵에 따르는 중
        설탕과 우유를 추가하는 중

        //tea output
        물 끓이는 중
        차를 우려내는 중
        컵에 따르는 중
        레몬를 추가하는 중
}

동일한 알고리즘 구조를 가진 prepareReceipe()메소드를 호출하였고, 커피와 차는 자신의 상태에 맞게 변형하여 적절한 아웃풋이 나왔습니다.

2. 클래스 다이어그램

3. JDK에서 템플릿 메소드 패턴 예시

AbstractMap<K,V>클래스를 보면 템플릿 메소드 패턴이 적용된 것을 볼 수 있다. 토비의 스프링에 나온 설명처럼 "변하지 않는 기능은 슈퍼클래스에 만들어두고 자주 변경되며 확장할 기능은 서브클래스에서 만들도록 한다."는 관점에서 보면, hashCode()메소드는 AbstractMap추상클래스에 있는 것을 사용하고, get()등 기타 많은 메소드들은 HashMap, TreeMap등 서브클래스에서 오버라이드 하여 재정의 하고 있는 것을 볼 수 있다.

  • AbstractMap의 hashCode()

public int hashCode() {
    int h = 0;
    for (Entry<K, V> entry : entrySet())
        h += entry.hashCode();
    return h;
}
  • AbstractMap의 get()

public V get(Object key) {
    Iterator<Entry<K,V>> i = entrySet().iterator();
    if (key==null) {
        while (i.hasNext()) {
            Entry<K,V> e = i.next();
            if (e.getKey()==null)
                return e.getValue();
        }
    } else {
       while (i.hasNext()) {
            Entry<K,V> e = i.next();
            if (key.equals(e.getKey()))
                return e.getValue();
        }
    }
    return null;
}
  • HashMap의 get()

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}
  • TreeMap의 get()

public V get(Object key) {
    Entry<K,V> p = getEntry(key);
    return (p==null ? null : p.value);
}

HashMap, TreeMap의 hashCode()메소드는 AbstractMap추상클래스의 상속을 받아 사용하고, get()메소드는 AbstractMap추상클래스의 상속을 받지만 HashMap, TreeMap각각 재정의 하여 사용합니다.

Reference

https://yaboong.github.io/design-pattern/2018/09/27/template-method-pattern/
http://wiki.gurubee.net/pages/viewpage.action?pageId=1507409
https://niceman.tistory.com/142

좋은 웹페이지 즐겨찾기