템플릿 메서드 패턴이란?
- 상위 클래스의 템플릿 메서드에서 하위 클래스가 오버라이딩한 메서드를 호출하는 패턴
- 객체지향 설계 5원칙 중 의존 역전 원칙(Dependency Inversion Principle)이 적용된 디자인 패턴
- 상위 클래스에 공통 로직을 수행하는 템플릿 메서드를 둔다.
- 템플릿 메서드 안에서 추상 메서드 또는 hook메서드를 호출한다.
- 하위 클래스마다 추상 메서드, hook메서드를 오버라이딩해 서로 다른 동작을 하게 한다.
- 상위 클래스 타입으로 하위 클래스 객체를 선언한다.
- 객체의 템플릿 메서드를 호출한다.
hook메서드
- 상위클래스에서 디폴트 기능을 정의해두거나 비워뒀다가 하위클래스에서 선택적으로 오버라이드할 수 있도록 만들어둔 메서드
- abstract 키워드가 없는 메서드
템플릿 메서드
- 상위클래스에 있으며 기본 알고리즘 골격을 담은 메서드
- 하위클래스에서 오버라이드하거나 구현할 메서드를 사용함
템플릿 메서드 패턴을 활용하면 객체마다 전체적으로는 같은 동작을 수행할때 부분적으로 다른 동작을 수행할 수 있다.
새로운 부분 동작을 수행하는 객체가 필요할때 부분 동작 구현만 신경쓰면 된다.
템플릿 메서드 패턴의 예시
예를 들어 햄버거를 만든다고 생각해보자.
- 햄버거에는 빵 -> 패티 -> 야채 -> 소스 -> 빵 순서로 햄버거를 만드는 템플릿 메서드가 있다.
- 햄버거에는 패티를 만드는 추상메서드가 있다.
- 치즈버거와 새우버거는 햄버거를 상속받고 패티를 만드는 메서드를 오버라이딩한다.
3-1. 치즈버거는 소고기 패티를 만드는 메서드
3-2. 새우버거는 새우 패티를 만드는 메서드 - 햄버거 타입으로 치즈버거 객체를 선언한다.
- 치즈버거의 햄버거 만드는 메서드를 호출하면 빵 -> 소고기패티 -> 야채 -> 소스 -> 빵 순서로 햄버거를 만든다.
템플릿 메서드 패턴 적용하기
우테코 프리코스 1주차에서 숫자를 생성하는 기능에 템플릿 메서드 패턴을 적용했다.
NumbersGenerator
에서generate()
는createNumbers()
의 반환값을Numbers
선언부에 넘겨줘 컬렉션 값을 반환한다.public abstract class NumbersGenerator { public List<Integer> generate() { Numbers numbers = new Numbers(createNumbers()); return numbers.getNumbers(); } abstract List<Integer> createNumbers(); }
- ComputerNumbersGenerator와 UserNumbersGenerator는
createNumbers()
를 오버라이딩해 각자만의 방식으로 생성한 List를 반환한다.
public class ComputerNumbersGenerator extends NumbersGenerator{
public List<Integer> createNumbers() {
List<Integer> computerNumbers = new ArrayList<>();
while (computerNumbers.size() < Constants.BASEBALL_NUMBER_SIZE) {
int randomNumber = Randoms.pickNumberInRange(Constants.COMPUTER_NUMBER_START, Constants.COMPUTER_NUMBER_END);
addRandomNumber(computerNumbers, randomNumber);
}
return computerNumbers;
}
private void addRandomNumber(List<Integer> computerNumbers, int randomNumber) {
if (!computerNumbers.contains(randomNumber)) {
computerNumbers.add(randomNumber);
}
}
}
public class UserNumbersGenerator extends NumbersGenerator{
public List<Integer> createNumbers() {
List<Integer> userNumbers = new ArrayList<>();
System.out.print("숫자를 입력해주세요 : ");
String userInput = readLine();
for (int i = 0; i < userInput.length(); i++) {
String str = userInput.substring(i, i + 1);
userNumbers.add(Integer.parseInt(str));
}
return userNumbers;
}
}
4. 각 객체를 사용한다.ComputerNumbersGenerator
는 생성자의 파라미터로 받도록 했다.
public class UnitOfGame extends Game {
private final List<Integer> computerNumbers;
public UnitOfGame(NumbersGenerator numbersGenerator) {
this.computerNumbers = numbersGenerator.generate();
}
...
public class UnitOfGameFactory implements GameFactory {
@Override
public UnitOfGame create() {
return new UnitOfGame(new ComputerNumbersGenerator());
}
}
UserNumbersGenerator
는 UnitOfGame
의 메서드에서 객체를 만들어 사용했다.
...
//UserNumbersGenerator
NumbersGenerator numbersGenerator = new UserNumbersGenerator();
List<Integer> userNumbers = numbersGenerator.generate();
...
이렇게 함으로써 같은 타입의 같은 메서드를 호출해도 다른 숫자를 받을 수 있었다.
그리고 숫자를 생성하는 방식이 바뀌면, 각 하위 클래스들의 createNumbers()
만 수정하면 된다.
템플릿 메서드 패턴의 대략적인 개념만 알고 있었는데
직접 적용해보니 어떻게 설계하고 사용해야되는지 알게 되었다!
'Java' 카테고리의 다른 글
Java 기초 문법(1) (0) | 2023.12.20 |
---|---|
Java 구동 방식 (1) | 2023.12.20 |
팩토리 메서드 패턴을 적용해보자 (0) | 2023.10.26 |
static 메서드는 언제 사용해야할까? (1) | 2023.10.26 |
일급 컬렉션을 어떻게 사용해야할까? (0) | 2023.10.26 |