| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |
Tags
- Lock-free Stack
- 이분 탐색
- 벨만-포드
- localstorage
- 강한 연결 요소
- PROJECT
- map
- trie
- Github
- 최소 공통 조상
- Binary Lifting
- DP
- 자바스크립트
- Spin Lock
- reference counting
- 트라이
- R 그래프
- Express.js
- ccw 알고리즘
- 비트마스킹
- SCC
- 게임 서버 아키텍처
- JavaScript
- Prisma
- 2-SAT
- 그래프 탐색
- Delete
- Strongly Connected Component
- 비트필드를 이용한 dp
- Behavior Design Pattern
Archives
- Today
- Total
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) 문서에 대해 일괄된 형식으로 의미 있는 데이터 추출
- 포맷에 맞게 처리하는 부분(알고리즘) 외에 많은 코드 중복(처리 과정)이 발생
- ex) 회사 문서들을 분석하는 앱을 만들고 있다고 가정

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

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

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

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



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



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

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

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

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

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

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

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


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

- 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가 고정됐을 때만)

- 단점
- 런타임에 동작을 결정하기 때문에 실행 시 오버헤드 발생 가능
- 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에게 라면을 부탁할지만 수정함
