템플릿 메서드 패턴 정리

youngjoon·2021년 9월 2일
0

Java

목록 보기
3/9
post-thumbnail

템플릿 메서드 패턴이란?

템플릿 메소드 패턴이란 메소드에서 알고리즘의 틀을 정의하고, 전체 혹은 일부단계를 서브클래스에서 이를 구현하는 디자인패턴 중 하나입니다.

1. 구현방법

제가 토이프로젝트로 만든 체스게임에서 이와 비슷한 코드를 소개하겠습니다. 사실 이때까지만 해도 디자인패턴에 대해 전혀 모르고 만들었습니다 ¯ࡇ¯

그저 당시엔 코드를 어떻게 하면 효율적으로 만들지 고민해보다가 만들어본건데 나중에 디자인패턴을 공부하고 다시보니 제코드가 템플릿메서드패턴과 비슷한다는 걸 알았습니다.

아래코드는 모든 기물의 추상클래스입니다. 모든 기물은 해당클래스를 상속받게 됩니다. 예제설명을 위해서 일부 코드는 생략했습니다.

게임 플레이어가 기물을 두면 해당 기물에게 행마하라는 요청을 합니다. 기물API 중 move()를 호출하면 validate()로 행마법이 올바른지 검증하게 됩니다. 아래 리스트는 모든 기물의 공통적으로 확인해야 하는 사항입니다.

  1. 기물은 원래위치로 이동이 불가능함
  2. 기물이 없거나 타겟이 킹인 경우(은 잡을 수 없음)
  3. 타겟기물이 동일 플레이어인 경우
  4. 상대방 기물을 선택한 경우

validate()에서 위의 리스트에 나와있는대로 공통적인 부분을 체크하고, checkPieceRange()에서 각 기물의 행마법에 따라 이동했는지 확인합니다. 모든 유효성검사가 완료되면 logic()에서 실제 행마를 하게 됩니다.

  • AbstractPiece.java
abstract class AbstractPiece implements Piece {
    private final PieceType pieceType;
    private final PlayerType playerType;

    AbstractPiece(PieceType pieceType, PlayerType playerType) {
        this.pieceType = pieceType;
        this.playerType = playerType;
    }

    public final PieceStatus move(ChessBoard board, PlayerType playerType, Position position, Position targetPosition) {
        if(validate(board, playerType, position, targetPosition)) {
            return logic(board, position, targetPosition);
        } else {
            return INVALID_MOVE;
        }
    }

    final boolean validate(ChessBoard board, PlayerType playerType, Position position, Position targetPosition) {
        Piece targetPiece = board.searchPiece(targetPosition);

        if(position.equals(targetPosition) || !board.validPiecePosition(targetPosition))
            return false;
        if(getPieceType() == NONE || targetPiece.getPieceType() == KING)
            return false;
        if(getPlayerType() == targetPiece.getPlayerType())
            return false;
        if(getPlayerType() != playerType)
            return false;

        return checkPieceRange(board, position, targetPosition);
    }

    abstract boolean checkPieceRange(ChessBoard board, Position position, Position targetPosition);

    abstract PieceStatus logic(ChessBoard board, Position position, Position targetPosition);

    abstract boolean isPossibleAttack(ChessBoard board, Position position, Position targetPosition);
}

여기서 중요한 사실은 기물을 배치할 때 전체 알고리즘은 AbstractPiece에 정의되어 있고, 서브클래스(모든 기물들)에서는 checkPieceRange(), logic()만 구현하면 됩니다.

만약 여기서 validate()를 모든 서브클래스에서 정의하고 사용한다면 중복코

2. Java API의 적용사례

Java API중에서 유사한 패턴을 찾아본다면 Map인터페이스의 sort()메서드가 해당됩니다. 아래는 실제 코드를 가져와봤습니다. 일부 코드는 생략했습니다.

  • Map.java
boolean containsKey(Object key);

V get(Object key);

default boolean replace(K key, V oldValue, V newValue) {
    Object curValue = get(key);
    if (!Objects.equals(curValue, oldValue) ||
        (curValue == null && !containsKey(key))) {
        return false;
    }
    put(key, newValue);
    return true;
}

Map인터페이스는 키(Key)를 값(Value)에 매핑하는 대표적인 자바의 자료구조입니다. 자주사용하는 구현체로는 Hashtable, HashMap, TreeMap이 있습니다.

replace()의 로직을 분석해보면 매개변수는 key(키값), oldValue(원래값), newValue(바꿀값) 3개의 파라미터를 받습니다. key()를 호출해 매핑된 값을 찾고, 두 값을 비교해서 같은 값이 아니거나 찾은값이 null이고 키가 검색되지 않는다면 실패하고, 조건에 맞다면 put()로 값을 바꾸게 됩니다.

여기서 중요한 건 해당 메서드의 전체 알고리즘을 구현하고, 중간 과정에서 get()containsKey()는 Map인터페이스를 구현하는 하위클래스에 구현합니다.

즉 메서드에서 모든 과정을 구현하지 않고 일부 로직만 하위에 맡기는 것으로 템플릿 메서드패턴을 적용했다고 할 수 있습니다.

profile
Java언어와 객체지향에 관심이 많은 개발자

0개의 댓글