Java언어로 배우는 디자인패턴 입문 정리 - 3. Template Method Pattern

양정훈·2021년 2월 1일
1
post-thumbnail

본 내용은 Java언어로 배우는 디자인패턴 입문(한빛미디어) 책을 보면서 정리한 내용입니다.

소개

  • 상위 클래스에서 처리의 흐름을 정하고 하위 클래스에서 구체적인 내용을 결정하는 패턴
  • 상위 클래스에 템플릿이 되는 메서드가 있고, 그 메소드의 정의에서 추상 메서드가 사용됨.
  • 실제 구현은 하위 클래스에서 이루어 지기 때문에, 하위 클래스가 정의 되어야 함.

실습예제

  • AbstractDisplay는 상위 클래스로 하위 클래스가 해야할 처리의 흐름이 정의 되어있다. 하위의 흐름을 정의하는 메서드는 display 메서드이다.
  • CharDisplay와 StringDisplay는 하위 클래스로써 구체적인 처리의 내용을 결정한다. AbstractDisplay를 상속받고 추상 메서드를 오버라이딩 하여 구현을 해낸다.

예제의 UML 다이어그램은 다음과 같다.

AbstractDisplay의 코드는 다음과 같다.

abstract class AbstractDisplay { //추상 클래스 AbstractDisplay
    public abstract void open(); //하위 클래스에 구현을 맡기는 추상메소드(1) open
    public abstract void print(); //하위 클래스에 구현을 맡기는 추상메소드(2) print
    public abstract void close(); //하위 클래스에 구현을 맡기는 추상메소드(3) close
    public final void display() { //추상 클래스에 구현하고 있는 메소드 display
        open(); //우선 open하고
        for(int i = 0; i < 5; i++) {
            print();
        }
        close(); // 마지막으로 close한다. 이것이 display 메소드에서 구현하고 있는 내용
    }
}

실제 구현인 open(), print(), close()는 추상 메서드로써 하위 클래스에게 구현하도록 하고, display() 메서드 에서는 메서드의 실행 흐름이 명시되어있다.
display() 메서드는 final 키워드를 이용하여, 오버라이딩 하지 못하게 한다. 그래야 모든 AbstractDisplay 클래스의 하위 클래스들이 올바른 템플릿을 사용하게 된다.

CharDisplay의 코드는 다음과 같다.

class CharDisplay extends AbstractDisplay { //CharDisplay는 AbstractDisplay의 하위 클래스
    private char ch;
    public CharDisplay(char ch) {
        this.ch = ch;
    }

    public void open() {
        System.out.print("<<");
    }

    public void print() {
        System.out.print(ch);
    }

    public void close() {
        System.out.println(">>");
    }
}

하위 클래스는 상위 메서드에서 제공했던 open(), print(), close()의 실제 구현을 담고 있다.

실제 코드를 사용하는 Main 클래스는 다음과 같다.

class Main {
    public static void main(String[] args) {
        AbstractDisplay d1 = new CharDisplay('H'); // 'H'를 가진 CharDisplay의 인스턴스를 1개 만든다.
        AbstractDisplay d2 = new StringDisplay("Hello, world."); // "Hello, world."를 가진 StringDisplay의 인스턴스를 1개 만든다.
        AbstractDisplay d3 = new StringDisplay("안녕하세요"); // "안녕하세요" 를 가진 StringDisplay의 인스턴스를 1개 만든다.

        d1.display();  // d1, d2, d3 모두 같은 AbstractDisplay의 하위 클래스의 인스턴스이기 때문에 상속한 display 메소드 호출 가능
        d2.display(); // 실제의 동작은 각 클래스 CharDisplay나 StringDisplay에서 정한다.
        d3.display();
    }
}

Main 클래스의 main 함수에서는 AbstractDisplay 객체에 CharDisplay(), StringDisplay() 인스턴스를 연결해서 선언한다.
그 후에 display() 메서드를 실행하는 식으로 template method pattern을 구현해본다.

UML 다이어그램

실제 패턴의 UML 다이어그램은 다음과 같다.

정리

  • AbstractClass의 역할
    - 구체적인 구현은 추상 메서드로 선언하여, 하위 클래스가 오버라이딩해서 사용하게 함.
    - templateMethod를 선언하고 실제 처리의 흐름을 정의한다. final 키워드를 사용해서 오버라이딩하지 못하게 한다.
  • ConcreteClass의 역할
    - 상위 클래스에서 선언한 추상 메서드의 실제 구현을 담당한다.
  • 사용 시
    - 상위클래스의 객체를 하위 인스턴스와 연결하고, templateMethod를 실행하게 한다.

활용법

  • 로직을 공통화할 수 있음
    - 여러개의 ConcreteClass를 만드는 방법도 생각할 수 있으나, 그러면 각 클래스마다 중복이 생긴다.
    - 중복은 버그의 잠재적인 원인이다. 하나의 클래스를 고쳤다고 생각해도 다른 클래스를 고치지 않으면 각 클래스마다 기대할 수 있는 결과가 다르기 때문에 디버깅 시에 효율이 매우 떨어질 수 있다.
    - 반면에 Template Method Pattern을 이용하며, 관심을 가져야 할 코드의 범위가 매우 줄어든다. 상위의 흐름이 문제라면 상위 클래스의 TemplateMethod만 보면 되고, 하위의 구현체에서 문제가 있다면 해당 구현체만 확인하면 된다.
  • 상위 클래스와 하위 클래스의 제휴
    - 상위 클래스에서 선언 되어있는 추상 메서드가 실제로 하위 클래스에서 구현될 때 어떤 타이밍에 호출되는지 이해할 필요가 있음.

    이 부분은 아직 이해가 안되서, 추후에 보강하겠음.

  • 하위 클래스와 상위 클래스의 동일시
    - 상위 클래스 형의 변수가 있고, 그 변수에 하위 클래스의 인스턴스가 대입되었을 때, instanceof 등으로 하위 클래스를 특별히 지정하지 않아도 프로그램이 동작하도록 하는 것이 중요함.
    • 이는 SOLID 원칙 중에서 L에 해당하는 리스코프 치환 법칙에 해당한다. 이는 상속의 일반적인 원리이다.

관련된 다른 패턴

  • Factory Pattern
    - Template Method Pattern을 객체 인스턴스 생성에 활용한 패턴
  • Strategy Pattern
    - Template Method Pattern은 상위 클래스에서 큰 흐름을 정하고 하위 클래스에서 구체적인 행동을 정의. 따라서 주로 상속을 이용
    - 반면에 Strategy Pattern은 알고리즘 전체를 교환하기 때문에 위엄 방식을 주로 이용

참고

하위 클래스의 책임

  • 상위 클래스는 하위 클래스가 자신의 추상 메서드들을 구현하기를 원함.
  • 하위 클래스에 대해 그 구현을 요청

추상 클래스의 의의

  • 인터페이스
    - 추상메서드 만을 가짐.
    - 인터페이스는 규약으로 강제성을 띔
    - 구현체가 인터페이스에 정의된 메서드를 모두 구현해야 함.
    - 즉 인터페이스는 해당 규약을 지킬 것을 요구하는 강제성을 띄어야 할 때 사용함.
  • 추상클래스
    - 둘 다 공통적으로 추상 메서드를 가질 수 있음.
    - 추상 클래스는 일반 메서드와 추상 메서드를 가질 수 있음.
    - 추상 클래스는 하위 클래스의 흐름을 정의하는 식으로 활용할 수 있음. 상위에 구현된 코드가 존재하기 떄문에 하위 클래스는 추상 클래스가 정의한 추상 메서드를 오버라이딩해서 구체적인 행동을 구현.
    - 추상 클래스는 자체 인스턴스를 가질 수는 없지만, 특정 구현은 하위 클래스가 구현할 필요 없이 그냥 가져다 쓸 수 있음.
    - 따라서 추상 클래스는 확장성을 고려해야 할 때 사용하면 적합.
profile
꿈을 현실로 만드는 성장형 인간

0개의 댓글