Mobile & App Stack/Java Software Architecture & Patterns

템플릿 메서드 패턴: 중복 코드를 줄이는 상속의 기술 (Java 예제)

임베디드 친구 2025. 1. 3. 10:57
728x90
반응형

소프트웨어를 개발하다 보면 비슷한 로직이 여러 클래스에서 반복되는 경우를 자주 마주칩니다. 예를 들어, 서로 다른 음료를 만들더라도 '물을 끓이고 컵에 붓는' 과정은 동일하죠. 이런 공통 로직을 매번 복사해서 붙여넣고 계신가요?

오늘은 알고리즘의 뼈대를 상위 클래스에 정의하고, 세부 단계만 하위 클래스에서 구현하는 템플릿 메서드 패턴(Template Method Pattern)에 대해 알아보겠습니다.

Generated by Gemini AI.


1. 템플릿 메서드 패턴이란?

템플릿 메서드 패턴은 여러 클래스에서 공통으로 사용하는 알고리즘의 구조를 상위 클래스(추상 클래스)에 정의하고, 구체적인 단계는 하위 클래스에서 구현하도록 하는 패턴입니다.

핵심 개념

  • 추상 클래스(Abstract Class): 알고리즘의 흐름을 제어하는 templateMethod를 정의합니다. 이 메서드는 하위 클래스에서 변경할 수 없도록 보통 final로 선언합니다.
  • 구체 클래스(Concrete Class): 상위 클래스에서 선언된 추상 메서드를 재정의하여 상세 로직을 완성합니다.
  • 할리우드 원칙 (Hollywood Principle): "먼저 연락하지 마세요. 저희가 연락하겠습니다." 즉, 저수준 구성 요소가 시스템에 접속하지만, 언제 어떻게 그들을 사용할지는 고수준 구성 요소(상위 클래스)가 결정합니다.

2. Java 실무 예제: 카페 음료 제조 시스템

Step 1. 상위 클래스 (알고리즘 뼈대)

Java
 
abstract class Beverage {
    // 템플릿 메서드: 전체적인 흐름을 정의 (final로 오버라이딩 방지)
    public final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) { // 후크(Hook) 메서드 활용
            addCondiments();
        }
    }

    private void boilWater() { System.out.println("물을 끓입니다."); }
    private void pourInCup() { System.out.println("컵에 붓습니다."); }

    // 하위 클래스가 반드시 구현해야 할 메서드
    protected abstract void brew();
    protected abstract void addCondiments();

    // 후크(Hook) 메서드: 필요에 따라 하위 클래스에서 오버라이드 가능
    protected boolean customerWantsCondiments() { return true; }
}

Step 2. 하위 클래스 (상세 구현)

Java
 
class Coffee extends Beverage {
    @Override
    protected void brew() { System.out.println("필터를 통해 커피를 내립니다."); }

    @Override
    protected void addCondiments() { System.out.println("설탕과 우유를 추가합니다."); }
}

class Tea extends Beverage {
    @Override
    protected void brew() { System.out.println("차를 우려냅니다."); }

    @Override
    protected void addCondiments() { System.out.println("레몬을 추가합니다."); }
    
    // 후크 메서드 재정의: 레몬을 원하지 않을 수도 있음
    @Override
    protected boolean customerWantsCondiments() { return false; } 
}

3. 템플릿 메서드 패턴의 장단점

👍 장점

  • 코드 중복 제거: 알고리즘의 공통 부분은 부모 클래스에서 한 번만 작성하므로 유지보수가 쉽습니다.
  • 확장성: 새로운 하위 클래스를 추가해도 상위 클래스의 알고리즘 구조를 건드리지 않아도 됩니다. (OCP 준수)
  • 로직 일관성: 모든 하위 클래스가 동일한 실행 순서를 따르도록 강제할 수 있습니다.

👎 단점

  • 상속의 한계: Java는 다중 상속이 불가능하므로, 이미 다른 클래스를 상속받은 경우 이 패턴을 사용할 수 없습니다.
  • 구조의 경직성: 상위 클래스의 알고리즘 구조가 바뀌면 모든 하위 클래스에 영향을 줄 수 있습니다.

4. 실무 활용 사례

  1. Java AbstractList: 작성해주신 것처럼 size(), get() 등 핵심 로직만 하위 클래스에 맡기고 나머지는 공통 로직을 사용합니다.
  2. Servlet의 service() 메서드: HttpServlet 클래스의 service() 메서드는 요청 방식에 따라 doGet(), doPost()를 호출하는 템플릿 역할을 합니다.
  3. Spring Framework: JdbcTemplate, RestTemplate 등 수많은 클래스에서 이 패턴을 사용하여 반복되는 리소스 해제나 로그 처리를 자동화합니다.

5. 꿀팁: 전략 패턴(Strategy)과 차이점

  • 템플릿 메서드 패턴: 상속을 이용해 로직을 확장합니다. (컴파일 타임에 결정)
  • 전략 패턴: 구성(Composition)을 이용해 로직을 주입합니다. (런타임에 동적으로 변경 가능)

결론

템플릿 메서드 패턴은 "변하지 않는 부분은 부모에게, 변하는 부분은 자식에게"라는 철학을 가장 잘 실현한 패턴입니다. 전체적인 흐름은 같지만 세부 구현이 다른 기능을 개발할 때, 이 패턴을 통해 깔끔하고 우아한 코드를 설계해 보세요!


포스팅이 도움이 되셨다면 공감과 구독 부탁드립니다! 여러분은 실무에서 어떤 반복 로직을 템플릿으로 만들고 싶으신가요? 댓글로 자유롭게 의견 나눠요!

반응형