dh_0e

[Design Pattern] Behavior Design Patterns V (Template Method Pattern, Visitor Pattern) 본문

Software Analysis & Design/Design Patterns

[Design Pattern] Behavior Design Patterns V (Template Method Pattern, Visitor Pattern)

dh_0e 2025. 12. 2. 11:27

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

  • 알고리즘의 구조(뼈대)를 상위 클래스에 정의하고, 알고리즘의 일부 단계는 서브클래스에서 구체적으로 구현할 수 있도록 하는 행동 패턴
  • 목적: 알고리즘의 구조를 유지한 채로 행동을 다르게 변경 가능
  • 필요한 상황
    • ex) 회사 문서들을 분석하는 앱을 만들고 있다고 가정
      • 다양한 포맷(pdf, doc, csv) 문서에 대해 일괄된 형식으로 의미 있는 데이터 추출
    • 포맷에 맞게 처리하는 부분(알고리즘) 외에 많은 코드 중복(처리 과정)이 발생

  • 아이디어
    • 알고리즘을 일련의 단계들로 나누고 이러한 단계를 메서드로 변환
    • 단일 템플릿 메서드 내부에 단계 메서드들에 대한 일련의 호출로 구성
    • 상속을 통해 추상 단계 메서드를 오버라이드 해서 구현

  • 상위 클래스에서 추상적인 메서드 틀을 짜고 하위 메소드에서 각 메서드를 구현하는 방식
  • 템플릿 메서드디폴트 단계 메서드의 구분: final의 유무

Template Method Pattern 구조

 

  • ex) 숫자를 읽어와 숫자들을 연산한 결과를 알려주는 기능 구현
    1. 모든 숫자를 더하는 기능(sum)
    2. 모든 숫자를 곱하는 기능(multiply)

Template Method Pattern example

  • process()가 본 알고리즘 순서이자 템플릿 메서드
  • calculate, getInitial이 추상 메서드로 하위 클래스(SumFileProcessor, MultiplyFileProcessor)에서 오버라이딩 되어 구현됨

Template Method Pattern 예시 코드(Abstract Class)
Template Method Pattern 예시 코드(Concrete Class)
Template Method Pattern(Client, Result)

 

훅(hook) 메서드

  • 몸체가 비어있는 단계 메서드자식 클래스가 선택적으로 오버라이딩할 수 있음
  • 템플릿 메서드는 훅이 오버라이딩이 되지 않아도 정상 작동
  • 훅 메서드는 알고리즘의 전/후에 배치되어 자식 클래스들에게 알고리즘의 추가 확장 지점을 제공
  • Default 단계 메서드와의 차이
    • 훅 메서드는 주로 텅 비어 있거나 단순히 true/false만 리턴함
    • default 단계 메서드는 실제로 동작하는 구체적인 로직이 들어있고, 꼭 필요할 때 Override함

hook 구조
hook 예시 코드
hook 예시 코드 (false나 true로 지정 가능)

 

  • 장점
    • 클라이언트가 대규모 알고리즘의 특정 부분만 재정의 하도록 하여 알고리즘의 다른 부분에 발생하는 변경에 영향을 덜 받도록 함
      • 상위 클래스(부모)가 힘든 일(복잡한 흐름 관리)을 도맡아 해 주니, 하위 클래스(자식)는 눈치 보지 않고 자기 할 일(세부 구현)만 하면 됨
    • 코드 간소화 및 관리 용이: 상위 클래스로 로직을 공통화하여 코드의 중복을 줄일 수 있고, 핵심 로직을 상위 클래스에서 관리하므로 관리가 용이해짐
  • 단점
    • 제공되는 뼈대로 인해 알고리즘 로직의 유연성이 제한 될 수 있음
    • 하위 클래스에서 구현할 때, 해당 단계 메서드가 어느 타이밍에 호출되는지 템플릿 메서드 로직을 이해할 필요가 있음
    • 로직에 변화가 생겨 상위 클래스가 수정되면 모든 서브 클래스 수정이 생김
      • ex) 라면 레시피

모든 서브 클래스 수정 상황 예시

  


 

방문자 패턴 (Visitor Pattern)

  • 알고리즘을 객체 구조에서 분리시키려는 행동 패턴
  • 목적: 각 클래스의 데이터 구조에서 처리 기능을 분리하여 별도의 클래스(방문자, Visitor)로 구현

  • 필요한 상황
    • ex) 그래프로 구성된 지리 정보를 사용해 작동하는 앱을 구현 중이라 가정
      • vertex는 산업, 관광 지역 등 세부적인 정보를 가지는 여러 클래스로 표현
      • 정점들은 도로로 연결됨
    • 그래프를 XML 형식으로 내보내는 작업을 구현하려고 할 때, 기존 노드 클래스를 변경할 수 없는 상황이라 가정
      • 각 노드 클래스에 export 메서드를 추가해서 그래프 순회하며 export를 수행하면 간단하게 처리할 수 있음

  • XML export 메서드를 모든 노드 클래스에 추가해야 하는데 이러한 변경과 함께 버그가 발생하면 전체 앱 오작동 가능
  • 노드 클래스의 주 작업은 지리 데이터를 처리하는 것이기에 export 추가가 적절치 않음
  • 만약 다른 형식으로 확장해야 한다면 클래스 전반적 수정 요구

  • 아이디어
    • 데이터를 처리하는 기능을 기존 클래스에 두는 것이 아닌, Visitor(방문자)라는 별도의 클래스에 배치
      • 방문자에 의해 방문되면서 처리 기능 수행
        (행동을 수행해야 했던 원래 객체가 방문자의 인수로 전달되면서 원래 객체의 정보에 접근)
    • XML export 예시에서 다음과 같이 노드 클래스 종류에 맞게 처리 기능을 구현하고 방문하면서 처리

Visitor Pattern 예시 코드(ConcreteVisitor, Elements, ConcreteElements)

  • 처음에 각 Node 클래스에 accept()는 추가해야 함
    • 최초 추가 후에 기능을 추가하고 싶다면 사용하고 싶은 기능의 Visitor(ex. JsonVisitor, PdfVisitor ..) 만들면 됨

 

  • ex) 도형(Elements) 클래스가 있고, 방문자(Visitor)는 도형의 넓이를 계산(ConcreteVisitors)하는 예시
    • ConcreteElements: Circle, Rectangle, and Triangle

Visitor Pattern 예시 코드(Visitor, Element, ConcreteElementA)
Visitor Pattern 예시 코드(ConcreteElementB, ConcreteElementC)

  • 각 this는 각 클래스를 의미하므로 accept()의 의미가 각자 다름

Visitor Pattern 예시 코드(ConcreteVisitor, Client)

  • Another Element(새로운 도형 추가): 오각형(Pentagon) 추가하고 싶다면 Pentagon 클래스를 Shape 구현해서 생성 후, Visitor(ShapeVisitor), 모든 ConcreteVisitors(AreaCalculator)에 넓이 계산 함수 선언 및 구현 추가
    • 단점: OCP 위반
  • Another Func(새 기능 추가): n각의 합을 구하는 함수(AngleCalculate)를 추가하고 싶다면
    AngleCalculate()를 ShapeVisitor 구현해서 만들고, 그 안에 각 visit 함수들 overriding 해서 구현, Client에서 AngleCalculate() visitor 만들어서 모든 Elements 방문
    • 장점: OCP 준수

 

  • 장점
    • 재사용성: Visitor 클래스는 관련된 동작을 캡슐화하므로 동일한 동작을 다양한 Element 클래스에 재사용할 수 있음
    • SRP 준수: 동작을 별도의 클래스(Visitor)로 캡슐화하므로 Element 클래스가 자신의 주된 책임에 집중함
    • OCP 준수: 다른 클래스를 변경하지 않으면서 새로운 행동 도입 가능 (Elements가 고정됐을 때만)

OCP 준수 상황 예시

  • 단점
    • 런타임에 동작을 결정하기 때문에 실행 시 오버헤드 발생 가능
    • Element 인터페이스를 구현하는 새로운 클래스가 추가될 때 Visitor에 대한 수정(새로운 visit 메서드 추가)이 발생하여 유지보수가 어려울 수 있음
      • 이땐 OCP 준수 X

 

 


 

Summary

  • 템플릿 메서드 패턴(Template Method Pattern)
    • 알고리즘 구조(뼈대)를 정의하고 일부 단계(팔)를 서브 클래스에서 구체적으로 구현할 수 있게 하는 행동 패턴
  • 방문자 패턴(Visitor Pattern)
    • 알고리즘을 객체 구조에서 분리시키려는 행위 패턴

 

Visitor Pattern을 사용하여 Template Method Pattern의 단점 보완

  • 둘의 조합은 실제로도 자주 쓰임
  • ex) Template Method Pattern의 단점에서 든 라면 레시피 예시에서 파 넣기()라는 단계를 추가한다고 가정

Ramen myRamen = new ShinRamen(); // 라면은 그대로!

// 1. 어제: 일반 요리사에게 맡김
myRamen.accept(new NormalChef()); 
// 결과: 그냥 신라면 나옴

// 2. 오늘: 파채 요리사에게 맡김 (여기만 바꾸면 끝!)
myRamen.accept(new GreenOnionChef()); 
// 결과: 파채 가득한 신라면 나옴
  • 다음과 같이 클라이언트에서 어떤 chef에게 라면을 부탁할지만 수정함