Computer Science/디자인 패턴

템플릿 메서드 패턴(Template Method Pattern)

TwinParadox 2021. 12. 18. 10:59
728x90

템플릿 메서드 패턴(Template Method Pattern)

슈퍼 클래스에서는 알고리즘의 구조를 정의한다.

서브 클래스에서는, 슈퍼 클래스가 정의한 구조는 변경하지 않고 그 내용만 오버라이딩한다. 

알고리즘이 동일하여 같은 역할을 하는 메서드들이 여러 벌 생성될 수 있지만, 세부 구현은 다른 형태로(DB만 다르거나, 주고 받는 내용만 다르거나...) 이루어진 경우에 유용하다.

 

대부분 어떤 것을 만드는 클래스들로(XXXMaker, XXXBuilder) 구현하면서 예시를 드는데, 샌드위치 메이커라는 컨셉으로 한 번 구현해봤다. 샌드위치는 안에 들어가는 재료가 저마다 다르고, 빵의 끝을 잘라낼 수도 있고, 먹기 편하게 아예 반으로 자를 수도 있고 다양한 형태로 조리될 수 있다. 만드는 과정이나 재료는 여러모로 더 복잡하겠지만, 크게 4가지 과정으로 간략하게 만들었다.

 

  1. 빵 추가
  2. 빵 자르기
  3. 재료 추가
  4. 소스 추가

 

SandwichMaker라는 추상 클래스를 통해 위에서 언급한 일련의 작업을 거치면 Sandwich를 생성하도록 했다.

 

Sandwich

import java.util.Map;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Sandwich {
	private String bread;
	private String vegetable;
	private String source;

	public Sandwich(Map<String, String> ingredients) {
		this.bread = ingredients.get("bread");
		this.vegetable = ingredients.get("vegetable");
		this.source = ingredients.get("source");
	}
}

 

SandwichMaker

import java.util.HashMap;
import java.util.Map;

import lombok.Getter;

@Getter
public abstract class SandwichMaker {
	private Map<String, String> ingredients;

	public SandwichMaker() {
		this.ingredients = new HashMap<>();
	}

	public final Sandwich makeSandwich() {
		System.out.println("---Make Start Sandwich---");

		addBread();
		cutBread();
		System.out.println("Done: Bread");

		addVegetable();
		System.out.println("Done: Vegetable");

		addSource();
		System.out.println("Done: Source");

		System.out.println("---Here you are---");
		return new Sandwich(ingredients);
	}

	public abstract void addBread();
	public abstract void cutBread();
	public abstract void addVegetable();
	public abstract void addSource();
}

 

ToastSandwichMaker

public class ToastSandwichMaker extends SandwichMaker {
	@Override
	public void addBread() {
		getIngredients().put("bread","Toasted Bread");
	}

	@Override
	public void cutBread() {
		System.out.println("Cut the end of Bread");
	}

	@Override
	public void addVegetable() {
		getIngredients().put("vegetable", "lettuce, tomato, onion");
	}

	@Override
	public void addSource() {
		getIngredients().put("source", "mustard");
	}
}

AvocadoSandwichMaker

public class AvocadoSandwichMaker extends SandwichMaker {
	@Override
	public void addBread() {
		getIngredients().put("bread","White Bread");
	}

	@Override
	public void cutBread() {
	}

	@Override
	public void addVegetable() {
		getIngredients().put("vegetable", "avocado, lettuce, tomato, onion");
	}

	@Override
	public void addSource() {
		getIngredients().put("source", "black pepper, ketchup");
	}
}

diagram

장점

  • 공통 로직을 추상 클래스에 정의하여 코드의 중복 감소
  • 서브 클래스의 역할 제한하여 핵심 로직 관리 용이
  • 의존 관계 역전을 통한 확장성 있는 코드 설계

 

단점

  • 메서드가 많아지면 클래스 관리 측면에서 까다로워진다.
  • 추상 클래스와 상속으로 출발하기 때문에, 클래스를 설계 이후에는 확장에 대한 유연성이 떨어진다.

 

 

어디에서 사용됐는가?

자바 코어 라이브러리에선 java.util.AbstractList, java.util.AbstractSet이 대표적인 예시다.

ArrayList, LinkedList 같은 List 구현체들이 AbstarctList을 확장하여 사용하고 있는데,

전체적인 diagram을 보면 다음과 같은 구조를 가지고 있다.

ArrayList, LinkedList

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable {
	...
}

public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable {
	...
}

두 클래스는 비슷한 메서드들을 가지고 있다. add(E element), indexOf(Object o) 같은 메서드들 다양한 메서드들이 AbstractList에 있는 것들을 오버라이드해서 사용하고 있다.

 

 

어떨 때 사용하는 게 좋을까?

  1. 서브 클래스에서 메서드 오버라이딩으로 다양한 동작을 구현하게 할 때
  2. 전체적인 알고리즘의 형태는 유지하고 세부 구현만 다르게 가져가야 할 때
  3. 일련의 작업이 중복되면서 조금만 다른 동작을 하는 클래스들이 여럿 보이는 경우

 

장점에서 취할 수 있는 부분들에 적합하다면 템플릿 메서드 패턴을 적용하는 것이 좋아보인다. 개인적인 경험으로는, 처음 설계 단계에서 1번과 2번의 조건을 충족해서 적용해보는 것이 좋을 것 같다. 이 글의 도입부에서 언급했던 것처럼, 다루는 데이터의 형태나 호출하여 데이터를 주고 받는 API나 DB만 다르고 전체적인 작업 흐름은 동일한 경우에 템플릿 메서드를 떠올리는 편이다.

리팩토링 단계에서 생각해본다면, 뭔가 비슷한 작업을 하는 클래스가 여럿 발견될 때도 1, 2번 조건을 갖추고 있는지 살펴본 후 적용해볼 수도 있을 것 같다.

 

 

Reference

https://www.baeldung.com/java-template-method-pattern

GoF의 디자인 패턴

https://docs.oracle.com/javase/7/docs/api/java/util/AbstractList.html

https://www.geeksforgeeks.org/template-method-design-pattern/

728x90