| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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
- SCC
- 최소 공통 조상
- localstorage
- 이분 탐색
- Delete
- 비트필드를 이용한 dp
- PROJECT
- map
- trie
- 그래프 탐색
- 비트마스킹
- 자바스크립트
- Spin Lock
- Behavior Design Pattern
- reference counting
- 2-SAT
- 게임 서버 아키텍처
- Binary Lifting
- Strongly Connected Component
- Prisma
- DP
- 벨만-포드
- ccw 알고리즘
- Express.js
- 강한 연결 요소
- Github
- 트라이
- Lock-free Stack
- R 그래프
- JavaScript
Archives
- Today
- Total
dh_0e
[Design Pattern] Behavior Design Patterns IV (Mediator Pattern, Memento Pattern, Observer Pattern) 본문
Software Analysis & Design/Design Patterns
[Design Pattern] Behavior Design Patterns IV (Mediator Pattern, Memento Pattern, Observer Pattern)
dh_0e 2025. 11. 27. 19:14중재자 패턴(Mediator Pattern)
- 정의: 객체 간의 직접 통신을 제한하고 중재자 객체(mediator)를 통해서만 협력하도록 하는 행동 패턴
- 중재자(Mediator): 객체 간의 통신을 관리하고 매개체 역할을 수행하여 객체 간의 결합을 낮추고 유연성을 확보

- 필요 상황
- 여러 객체들이 상호작용하는 복잡한 상황에서 각 객체가 서로를 참조하게 되어 한 클래스 수정 시 연관된 클래스들의 수정이 연쇄적으로 발생할 때 필요
- ex) 프로필 편집 대화 상자

- 아이디어
- 각 객체 간의 연결을 느슨하게 만들어야 함
- 객체 간의 직접적인 통신은 중단하고, 이러한 호출을 대신 처리할 수 있는 중재제 객체를 두어서 간접적으로 협력시킴
- M:N의 관계를 M:1의 관계로 전환
- 각 객체 간의 연결을 느슨하게 만들어야 함


| 구성 요소 | 역할 | 상세 설명 |
| 1. Components | 중재자 참조를 통해 소통 | 컴포넌트들은 서로를 인식하지 않아야 하며, 일이 발생하면 중재자에게만 알려야 합니다. |
| 2. Mediator | notify 메서드를 통해 중재 | 컴포넌트들을 다른 중재자에 연결하여 재사용할 수 있습니다. |
| 3. Concrete Mediator | 컴포넌트 간의 관계 캡슐화 | 중재자가 관리하는 컴포넌트의 참조를 유지하며, 다양한 컴포넌트 간의 관계를 캡슐화합니다. |

- sendMessage가 핵심임

- 장점
- 다양한 컴포넌트 간의 통신을 한 곳으로 추출하여 코드 이해 및 유지관리가 쉬워짐
- 프로그램 컴포넌트 간의 결합도를 줄일 수 있음
- 개별 컴포넌트들을 더 쉽게 재사용할 수 있음
- OCP 준수: 실제 컴포넌트들을 변경하지 않아도 새로운 중재자를 도입할 수 있음
- 단점
- 중재자가 너무 많은 책임을 지게 되어 GodObject로 발전할 수 있음 & SRP 위반
퍼시드 패턴과의 차이점
- 퍼사드 패턴: 라이브러리(or 서브시스템)에 대해 사용하기 편하기 간편한 인터페이스를 구성하기 위한 구조 패턴
| 특징 | Mediator Pattern (중재자) | Facade Pattern (퍼사드) |
| 목적 | 시스템 컴포넌트 간의 통신을 중앙 집중화 | 하위 시스템에 대한 단순화된 인터페이스 정의 |
| 객체 인지 | 컴포넌트들은 중재자를 인식하고 통신 | 하위 시스템 자체는 퍼사드를 인식하지 못함 |
| 내부 통신 | 중재자를 통해서만 간접 통신 유도 | 하위 시스템 내 객체들은 서로 직접 통신 가능 |
| 하위 시스템 통신 | 하위 시스템 내 객체들의 통신에 관여함 | 하위 시스템 내 객체들의 통신에 관여할 수 없음 |
메멘토 패턴(Memento Pattern)
- 정의: 객체의 구현 세부 사항을 공개하지 않으면서 해당 객체의 이전 상태값을 저장하고 복원할 수 있게 해주는 행동 패턴
(= 스냅샷)

- 필요 상황
- 텍스트 편집기 등에서 실행 취소(Undo) 기능을 구현할 때, 특정 작업 수행 전에 객체의 상태를 기록하고, 실행 취소 시 최신 스냅샷을 가져와 복원해야 함

- 문제점
- 이를 위해서 객체의 모든 필드를 살펴본 후 해당 값들을 복사해야 함
- 그러나 대부분의 실제 객체들은 모든 중요한 데이터를 비공개함 (캡슐화)
- 공개된 필드라고 할지라도, 객체 일부가 수정되면 복사를 맡은 클래스들 역시 변경이 되어야 함

- 아이디어
- 상태 스냅샷들의 생성을 해당 상태의 실제 소유자인 originator 객체에 위임
- memento라는 특수 객체에 복사본을 저장
- 메멘토의 내용에는 메멘토를 생성한 객체를 제외한 다른 어떤 객체도 접근 불가


| 구성 요소 | 역할 | 특징 |
| 1. Originator | 자신의 상태에 대한 스냅샷 생성 및 복원 | private 필드에 직접 접근할 수 있도록 메멘토 클래스를 내부에 중첩하여 설계할 수 있습니다. |
| 2. Memento | 스냅샷 역할 | 관행적으로 불변(immutable)으로 만들고 생성자를 통해 값을 저장합니다. |
| 3. Caretaker | 메멘토들의 스택 저장 | 오리지네이터의 기록을 추적하며, 복원 시 사용할 메멘토 객체들을 관리합니다. |

- 중첩 클래스는 바깥 클래스의 private 멤버에 접근할 수 있음(Java/C#)
- Snapshot에서 Editor의 private 멤버에 접근 가능
- 메멘토를 내부 중첩으로 설계하는 이유는 기능 때문이라기보다 캡슐화를 지키기 위한 설계 트릭
- 외부엔 메멘토를 불투명한 토큰처럼 보이게 하고, Originator만 복원할 수 있게 만드는 것

- undo에서 사용하는 restore: 이전 상태를 param으로 받아 현 상태를 바꿔줌
- this.stack = new Stack<>(); - 자료형 선언할 때 적었으니 생략 가능

// 출력 결과
design pattern 1 - Memento
design pattern 2 - Iterator
design pattern 3 - Prototype
=== 복구 ===
design pattern 3 - Prototype
design pattern 2 - Iterator
design pattern 1 - Memento
- 장점
- 캡슐화를 위반하지 않고 객체의 상태 스냅샷을 생성 가능
- 케어테이커가 상태 기록을 유지(Stack)하도록 하여 오리지네이터의 코드를 단순화할 수 있음
- 저장본들 관리할 필요 없이 상태 저장/복원 기능만 구현하면 됨
- 사실 정석으론 내용 확인도 못함 오리지네이터만 내용 확인 가능, 케어테이커는 껍데기만 들고 있는거
- 정석: Snapshot을 가짜 자료형인 Memento로 구현받 사용 (업캐스팅: Upcasting)
// 정석 코드 (command가 Snapshot 확인 못하고 들고만 있다가 Editor(Originator)에게 전달)
public interface Memento {
// 텅 비어있음 (내용물을 숨기기 위한 마커)
}
public class Editor {
// ★ private으로 선언하면 Command는 이 클래스의 존재조차 모름 (Type도 못 씀)
private static class Snapshot implements Memento { ... }
public Memento create() {
return new Snapshot(...); // 밖에는 껍데기(인터페이스)만 던져줌
}
}
public class Command {
// [핵심 4] Snapshot 타입을 못 쓰니까 Memento(껍데기)로 스택을 만듦
private Stack<Memento> history = new Stack<>();
public void makeBackup(Editor editor) {
// 받아올 때도 Memento 타입으로 받음
Memento m = editor.create();
history.push(m);
}
public void undo(Editor editor) {
if (history.isEmpty()) return;
// 꺼낼 때도 Memento 타입
Memento m = history.pop();
// 내용물은 못 보지만, 주인(Editor)한테 그대로 돌려줌
editor.restore(m);
}
}
- 단점
- 클라이언트가 메멘토를 너무 자주 생성하면 메모리 사용이 증가
- 케어테이커에게 오래된 메멘토를 삭제하고 관리하는 역할이 추가될 수 있음
- 클라이언트가 메멘토를 너무 자주 생성하면 메모리 사용이 증가
옵저버 패턴(Observer Pattern)
- 정의: 옵저버들이 관찰하고 있는 대상(Publisher/Subject)의 상태 변화가 있을 때마다 대상자는 각 관찰자(Observer/Subscriber)에게 통지하고, 관찰자들은 알림을 받아 조치를 취하는 행동 패턴(= 발행-구독 모델)
- ex) 유튜버 구독해 놓고 알림 켜놨다고 생각하면 됨


| 구성 요소 | 역할 | 상세 설명 |
| 1. Publisher (관찰대상자) | 구독자 관리 및 이벤트 발행 | 상태가 바뀌면 변경 사항을 관찰자에게 통보합니다. 인터페이스로 분리할 수도 있습니다. |
| 3. Subscriber (관찰자) | 알림 인터페이스 선언 및 업데이트 | 통보를 받은 관찰자는 필요에 맞추어 적절하게 업데이트를 수행합니다. |
| 2. 알림 (Notification) | 이벤트 발생 시 전송 | Publisher는 새 이벤트 발생 시 각 구독자 객체에게 알림을 보냅니다. 구독 추가/취소는 언제든지 가능합니다. |
- 옵저버 패턴의 흐름
- 한 개의 관찰 대상자와 여러 개의 관찰자로 1:N 관계 구성
- 관찰 대상자의 상태가 바뀌면 변경사항을 관찰자에게 통보
- 통보를 받은 관찰자는 적절하게 필요에 맞추어 업데이트
- 언제든지 구독 추가/취소 가능



- 구조 다이어그램엔 없지만 Publisher가 Interface, WeatherAPI가 Concrete publisher라고 생각하면 됨
- 장점
- 관찰 대상자(Publisher)의 상태 변경을 주기적으로 조회하지 않고 자동으로 감지
- Publisher의 코드를 변경하지 않고도 새 구독자(Subscriber) 클래스를 쉽게 도입할 수 있음
- 런타임 시점에 발행자와 구독 알림 관계를 맺음
- 상태 변경하는 객체(Publisher)와 변경을 감지하는 객체(Subscriber)의 관계를 느슨하게 유지
- 단점
- 구독자는 알림 순서를 제어할 수 없고 무작위로 통보만 받음
- 사용하지 않는 Observer 객체를 해지하지 않으면 메모리 낭비가 발생할 수 있음
Summary
- 중재자 패턴(Mediator Pattern)
- 객체 간의 직접 통신을 제한하고 중재자 객체를 통해서만 협력하도록 하는 행동 패턴
- 메멘토 패턴(Memento Pattern =Snapshot)
- 객체의 구현 세부 사항을 공개하지 않으면서 해당 객체의 이전 상태 값을 저장하고 복원할 수 있게 해주는 행동 패턴
- 옵저버 패턴(Observer Pattern)
- 옵저버(Subscriber)들이 관찰하고 있는 대상(Publisher)의 상태 변화가 있을 때마다 대상자(Publisher)는 직접 각 관찰자(Subscriber)에게 통지하고 관찰자들은 알림을 받아 조치를 취하는 행동 패턴
