// ❌ 나쁜 예: 객체들이 서로 직접 통신하는 코드
public class ListBox {
private EntryField entryField;
private Button okButton;
private Button cancelButton;
public void onSelectionChanged() {
// ListBox가 모든 다른 객체들을 알아야 함
entryField.setText(getSelection());
okButton.setEnabled(true);
cancelButton.setEnabled(true);
}
}
public class EntryField {
private ListBox listBox;
private Button okButton;
public void onTextChanged() {
// EntryField도 다른 객체들을 알아야 함
listBox.selectItem(getText());
okButton.setEnabled(!getText().isEmpty());
}
}
문제점:
public interface Mediator {
void mediate(); // 중재 메서드
}
왜 Interface인가?
public abstract class Colleague {
protected Mediator mediator;
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
}
왜 Abstract Class/Interface인가?
다양한 중재 방식
// ✅ 가능: 다양한 Dialog마다 다른 중재 로직
public class FontDialogDirector implements Mediator {
// 폰트 다이얼로그 전용 중재 로직
}
public class FileDialogDirector implements Mediator {
// 파일 다이얼로그 전용 중재 로직
}
공통 구현이 없음
Colleague (N) ────────> Mediator (1) ────────> Colleague (N)
(통신 요청자들) (통신 중재자) (통신 수신자들)
- 다대일대다 관계
- Colleague는 서로를 모르고 Mediator만 앎
- Mediator가 모든 통신을 중재
- 통신 로직이 Mediator에 집중됨
항공 관제탑 비유:
// Mediator 인터페이스
public interface DialogDirector {
void showDialog();
void widgetChanged(Widget widget);
}
// Colleague 추상 클래스
public abstract class Widget {
protected DialogDirector director;
public Widget(DialogDirector director) {
this.director = director;
}
// 변경 사항을 Director에게 알림
public void changed() {
director.widgetChanged(this);
}
}
public class FontDialogDirector implements DialogDirector {
private ListBox fontList;
private EntryField fontField;
private Button okButton;
@Override
public void showDialog() {
// 위젯들 생성
fontList = new ListBox(this);
fontField = new EntryField(this);
okButton = new Button(this);
// 초기 상태 설정
okButton.setEnabled(false);
}
@Override
public void widgetChanged(Widget widget) {
// ListBox가 변경됨
if (widget == fontList) {
String selection = fontList.getSelection();
fontField.setText(selection);
okButton.setEnabled(true);
}
// EntryField가 변경됨
else if (widget == fontField) {
String text = fontField.getText();
if (!text.isEmpty()) {
fontList.selectItem(text);
okButton.setEnabled(true);
} else {
okButton.setEnabled(false);
}
}
// Button이 클릭됨
else if (widget == okButton) {
System.out.println("폰트 적용: " + fontField.getText());
}
}
}
// ListBox 구현
public class ListBox extends Widget {
private List<String> items = new ArrayList<>();
private String selectedItem;
public ListBox(DialogDirector director) {
super(director);
}
public void addItem(String item) {
items.add(item);
}
public String getSelection() {
return selectedItem;
}
public void selectItem(String item) {
this.selectedItem = item;
changed(); // Mediator에게 알림!
}
}
// EntryField 구현
public class EntryField extends Widget {
private String text = "";
public EntryField(DialogDirector director) {
super(director);
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
System.out.println("EntryField 업데이트: " + text);
}
public void userTyped(String text) {
this.text = text;
changed(); // Mediator에게 알림!
}
}
// Button 구현
public class Button extends Widget {
private boolean enabled = false;
public Button(DialogDirector director) {
super(director);
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
System.out.println("Button " + (enabled ? "활성화" : "비활성화"));
}
public void click() {
if (enabled) {
changed(); // Mediator에게 알림!
}
}
}
public class MediatorDemo {
public static void main(String[] args) {
// Mediator 생성
FontDialogDirector director = new FontDialogDirector();
director.showDialog();
// 사용자가 ListBox에서 항목 선택
System.out.println("\n=== ListBox에서 'Arial' 선택 ===");
ListBox fontList = director.getFontList();
fontList.addItem("Arial");
fontList.addItem("Times New Roman");
fontList.addItem("Courier");
fontList.selectItem("Arial");
// 사용자가 EntryField에 텍스트 입력
System.out.println("\n=== EntryField에 'Courier' 입력 ===");
EntryField fontField = director.getFontField();
fontField.userTyped("Courier");
// 사용자가 OK 버튼 클릭
System.out.println("\n=== OK 버튼 클릭 ===");
Button okButton = director.getOkButton();
okButton.click();
}
}
Button 비활성화
=== ListBox에서 'Arial' 선택 ===
EntryField 업데이트: Arial
Button 활성화
=== EntryField에 'Courier' 입력 ===
Button 활성화
=== OK 버튼 클릭 ===
폰트 적용: Courier
// 채팅방 Mediator
public class ChatRoom {
private Map<String, User> users = new HashMap<>();
public void register(User user) {
users.put(user.getName(), user);
}
public void sendMessage(String message, String fromUser) {
// 모든 사용자에게 메시지 전송 (보낸 사람 제외)
for (User user : users.values()) {
if (!user.getName().equals(fromUser)) {
user.receive(fromUser + ": " + message);
}
}
}
}
// User Colleague
public class User {
private String name;
private ChatRoom chatRoom;
public User(String name, ChatRoom chatRoom) {
this.name = name;
this.chatRoom = chatRoom;
chatRoom.register(this);
}
public String getName() {
return name;
}
public void send(String message) {
System.out.println(name + " 전송: " + message);
chatRoom.sendMessage(message, name);
}
public void receive(String message) {
System.out.println(name + " 수신: " + message);
}
}
// 실행
public class ChatDemo {
public static void main(String[] args) {
ChatRoom chatRoom = new ChatRoom();
User alice = new User("Alice", chatRoom);
User bob = new User("Bob", chatRoom);
User charlie = new User("Charlie", chatRoom);
alice.send("안녕하세요!");
bob.send("반갑습니다!");
}
}
출력:
Alice 전송: 안녕하세요!
Bob 수신: Alice: 안녕하세요!
Charlie 수신: Alice: 안녕하세요!
Bob 전송: 반갑습니다!
Alice 수신: Bob: 반갑습니다!
Charlie 수신: Bob: 반갑습니다!
| 측면 | Mediator Pattern | Observer Pattern |
|---|---|---|
| 통신 방식 | 중앙 집중식 (Centralized) | 분산식 (Distributed) |
| 관계 | 다대일대다 (Colleagues ↔ Mediator) | 일대다 (Subject → Observers) |
| 통신 흐름 | Mediator가 명시적으로 제어 | Subject가 자동으로 모든 Observer에게 알림 |
| 결합도 | Colleague는 Mediator만 앎 | Observer는 Subject를 알아야 함 |
| 재사용성 | Mediator는 재사용 어려움 | Observer/Subject는 재사용 쉬움 |
| 이해도 | 통신 흐름 이해 쉬움 | 통신 흐름 추적 어려움 |
| 사용 시기 | 복잡한 객체 간 상호작용 조정 | 데이터 변경 시 여러 객체 자동 업데이트 |
| 요소 | 역할 | 특징 |
|---|---|---|
| Mediator | Colleague 간 통신 중재 | 통신 로직이 집중됨 |
| Colleague | Mediator를 통해 통신 | 서로를 직접 알지 못함 |
| Concrete Mediator | 실제 중재 로직 구현 | 특정 상황에 맞춤 |
| Concrete Colleague | 실제 기능 수행 | Mediator를 통해서만 통신 |
장점 (+)
단점 (-)