중재자 패턴이란?
객체들 간의 상호작용을 중재하는 패턴이다.
객체들끼리 직접 통신하는 방식이 아닌 중재자를 통해서 상호작용하도록 하는 방식이다.
항공기 관제탑을 보면 이해할 수 있다. 비행기끼리 통신을 할 수는 있지만 모든 통신을 각자 하게 되면 통신을 받지 못하는 비행기도 생기고, 통신이 매우 난잡해질 것이다. 그래서 모든 비행기들은 관제탑을 통해서 통신을 하면서 조율을 하게 된다.
중재자 패턴을 왜 사용해야 하는가?
- 객체 간의 결합도를 낮춰준다.
- 새로운 객체들을 도입할 수 있다.
- 단일책임원칙을 지켜 유지보수와 재사용을 쉽도록 만든다.
패턴을 언제 사용해야 하는가?
- 객체들이 서로 복잡하고 강하게 결합되어 있는 경우
- 객체들간 직접적인 상호작용이 코드를 복잡하게 만들어 유지보수가 어려운 경우
문제점
- 중재자가 슈퍼맨이 될 수 있다. 즉, 중재자가 너무 많은 책임을 갖게 될 수 있다는 것이다.
- 중재자를 통해서 간접적으로 상호작용하기 때문에 성능이 비교적 느려질 수 있다.
구현 예시(자바)
패턴을 적용하지 않았을 때
먼저 중재자 패턴 없이 각 객체 간에 통신을 직접 만들 때를 예로 들었다.
간단한 채팅을 구현해보는 것을 예시로 한다. (실제 각자 화면이 아닌 콘솔상에서...)
먼저 채팅을 할 유저를 구현해 준다.
package com.example.designpattern.behavioral.mediator.nopattern;
public class User {
private String name;
public User(String name) {
this.name = name;
}
public void sendMessage(String msg, User toUser){
toUser.receiveMessage(msg, this);
}
public void receiveMessage(String msg, User fromUser){
System.out.println(fromUser.name + " >> " + this.name + ": " + msg);
}
}
간단하게 이름을 가지고 있고 메시지 전송과 수신 두 가지 동작을 할 수 있는 객체이다.
이제 이 유저들이 서로 채팅을 한다고 가정하고 메인 메서드를 구현해 보자.
package com.example.designpattern.behavioral.mediator.nopattern;
import java.util.ArrayList;
import java.util.List;
public class NoMediatorClient {
public static void main(String[] args) {
// 방 생성
List<User> userList = new ArrayList<>();
// 유저 입장
User user1 = new User("user1");
User user2 = new User("user2");
User user3 = new User("user3");
User user4 = new User("user4");
userList.addAll(List.of(user1, user2, user3, user4));
sendToAll(user1, "안녕 애들아", userList);
// 귓속말
user1.sendMessage("user2야 나랑 놀자", user2);
user2.sendMessage("그래 user1아", user1);
}
private static void sendToAll(User from, String msg, List<User> userList){
for (User user : userList) {
if (from != user) {
from.sendMessage(msg, user);
}
}
}
}
현재 User 클래스만을 사용했는데도 복잡해지는 모습을 볼 수 있다. 여기서 만약 익명 유저와 어드민이 추가된 상태에서 구현한다면...? 매우 복잡해져서 감당하기가 힘들어질 것이다..
중재자 패턴 적용기
이번엔 중재자 패턴을 적용시켜서 구현해 보자
일반 유저, 익명 유저, 관리자 유저를 만들어보자
세 유저를 추상화여 User 클래스를 먼저 만들어준다.
package com.example.designpattern.behavioral.mediator.pattern.user;
import com.example.designpattern.behavioral.mediator.pattern.mediator.Mediator;
import lombok.Getter;
public class User {
@Getter
private String name;
protected Mediator mediator;
public User(String name, Mediator mediator) {
this.name = name;
this.mediator = mediator;
}
public void sendMessageAll(String msg){
mediator.sendMessageAll(this, msg);
}
public void sendMessage(User to, String msg){
mediator.sendMessage(to, this, msg);
}
public void receiveMessage(User from, String msg){
System.out.println(from.getName() + " >> " + this.name + ": " + msg);
}
}
그다음 각각의 유저들을 구현해 준다.
이때 익명의 유저는 1:1 대화 전송을 불가능하도록 하고 관리자계정은 관리자가 보내는 메시지임을 알 수 있도록 효과를 넣어준다.
package com.example.designpattern.behavioral.mediator.pattern.user;
import com.example.designpattern.behavioral.mediator.pattern.mediator.Mediator;
public class NormalUser extends User {
public NormalUser(String name, Mediator mediator) {
super(name, mediator);
}
}
package com.example.designpattern.behavioral.mediator.pattern.user;
import com.example.designpattern.behavioral.mediator.pattern.mediator.Mediator;
public class AnonymousUser extends User{
public AnonymousUser(String name, Mediator mediator) {
super(name, mediator);
}
@Override
public void sendMessage(User to, String msg) {
System.out.println("익명 유저는 메시지를 전송할 수 없습니다.");
}
}
package com.example.designpattern.behavioral.mediator.pattern.user;
import com.example.designpattern.behavioral.mediator.pattern.mediator.Mediator;
public class AdminUser extends User{
public AdminUser(String name, Mediator mediator) {
super(name, mediator);
}
@Override
public void sendMessageAll( String msg) {
super.sendMessageAll("[ADMIN] **" + msg + "**");
}
@Override
public void sendMessage(User to, String msg) {
super.sendMessage(to, "[ADMIN] **" + msg + "**");
}
public void kickUser(User user, String cause) {
this.sendMessageAll(user.getName() + "를 " + cause + "의 원인으로 추방합니다.");
mediator.removeUser(user);
}
}
이제 각 유저들 간의 통신을 담당할 중재자를 구현하여 준다.
package com.example.designpattern.behavioral.mediator.pattern.mediator;
import com.example.designpattern.behavioral.mediator.pattern.user.User;
public interface Mediator {
void sendMessageAll(User from, String msg);
void sendMessage(User from, User to, String msg);
void addUser(User user);
void removeUser(User user);
}
package com.example.designpattern.behavioral.mediator.pattern.mediator;
import com.example.designpattern.behavioral.mediator.pattern.user.User;
import java.util.ArrayList;
import java.util.List;
public class ChatMediator implements Mediator {
private final List<User> users;
public ChatMediator() {
this.users = new ArrayList<>();
}
@Override
public void sendMessageAll(User from, String msg) {
for (User user : users) {
if(user == from) continue;
user.receiveMessage(from, msg);
}
}
@Override
public void sendMessage(User from, User to, String msg) {
to.receiveMessage(from, msg);
}
@Override
public void addUser(User user) {
sendMessageAll(user, user.getName() + "님이 접속하였습니다.");
this.users.add(user);
}
@Override
public void removeUser(User user) {
sendMessageAll(user, user.getName() + "님이 퇴장하였습니다.");
this.users.add(user);
}
}
중재자를 이용해서 각 객체 간에 상호작용을 구현해 보자.
package com.example.designpattern.behavioral.mediator.pattern;
import com.example.designpattern.behavioral.mediator.pattern.mediator.ChatMediator;
import com.example.designpattern.behavioral.mediator.pattern.mediator.Mediator;
import com.example.designpattern.behavioral.mediator.pattern.user.AdminUser;
import com.example.designpattern.behavioral.mediator.pattern.user.NormalUser;
import com.example.designpattern.behavioral.mediator.pattern.user.AnonymousUser;
import com.example.designpattern.behavioral.mediator.pattern.user.User;
public class MediatorClient {
public static void main(String[] args) {
Mediator mediator = new ChatMediator();
User anonymousUser = new AnonymousUser("익명유저", mediator);
AdminUser admin = new AdminUser("어드민", mediator);
User user1 = new NormalUser("노말유저1", mediator);
User user2 = new NormalUser("노말유저2", mediator);
User user3 = new NormalUser("노말유저3", mediator);
System.out.println("========= 익명유저 입장 =========");
mediator.addUser(anonymousUser);
System.out.println("========= 어드민 입장 =========");
mediator.addUser(admin);
System.out.println("========= 유저들 입장 =========");
mediator.addUser(user1);
mediator.addUser(user2);
mediator.addUser(user3);
System.out.println("========= 유저1 메시지 =========");
user1.sendMessageAll("안녕하세요!");
System.out.println("========= 유저1 <-> 유저2 =========");
user1.sendMessage(user2, "안녕 user2야");
user2.sendMessage(user1, "반가워 suer1아");
System.out.println("========= 어드민 공지 =========");
admin.sendMessageAll("반가워요");
System.out.println("========= 추방하기 =========");
admin.kickUser(user2, "도배");
System.out.println("========= 익명유저 -> 유저1 =========");
anonymousUser.sendMessage(user1, "난 익명이지롱 바부야");
}
}
이전 패턴을 적용하기 전에는 직접 메서드를 만들어 구현을 했어야 했지만 이제는 중재를 통해서 통신을 하기 때문에 손쉽게 객체를 추가하고 변경할 수 있게 되었다.
실행결과는 다음과 같다
========= 익명유저 입장 =========
========= 어드민 입장 =========
어드민 >> 익명유저: 어드민님이 접속하였습니다.
========= 유저들 입장 =========
노말유저1 >> 익명유저: 노말유저1님이 접속하였습니다.
노말유저1 >> 어드민: 노말유저1님이 접속하였습니다.
노말유저2 >> 익명유저: 노말유저2님이 접속하였습니다.
노말유저2 >> 어드민: 노말유저2님이 접속하였습니다.
노말유저2 >> 노말유저1: 노말유저2님이 접속하였습니다.
노말유저3 >> 익명유저: 노말유저3님이 접속하였습니다.
노말유저3 >> 어드민: 노말유저3님이 접속하였습니다.
노말유저3 >> 노말유저1: 노말유저3님이 접속하였습니다.
노말유저3 >> 노말유저2: 노말유저3님이 접속하였습니다.
========= 유저1 메시지 =========
노말유저1 >> 익명유저: 안녕하세요!
노말유저1 >> 어드민: 안녕하세요!
노말유저1 >> 노말유저2: 안녕하세요!
노말유저1 >> 노말유저3: 안녕하세요!
========= 유저1 <-> 유저2 =========
노말유저2 >> 노말유저1: 안녕 user2야
노말유저1 >> 노말유저2: 반가워 suer1아
========= 어드민 공지 =========
어드민 >> 익명유저: [ADMIN] **반가워요**
어드민 >> 노말유저1: [ADMIN] **반가워요**
어드민 >> 노말유저2: [ADMIN] **반가워요**
어드민 >> 노말유저3: [ADMIN] **반가워요**
========= 추방하기 =========
어드민 >> 익명유저: [ADMIN] **노말유저2를 도배의 원인으로 추방합니다.**
어드민 >> 노말유저1: [ADMIN] **노말유저2를 도배의 원인으로 추방합니다.**
어드민 >> 노말유저2: [ADMIN] **노말유저2를 도배의 원인으로 추방합니다.**
어드민 >> 노말유저3: [ADMIN] **노말유저2를 도배의 원인으로 추방합니다.**
노말유저2 >> 익명유저: 노말유저2님이 퇴장하였습니다.
노말유저2 >> 어드민: 노말유저2님이 퇴장하였습니다.
노말유저2 >> 노말유저1: 노말유저2님이 퇴장하였습니다.
노말유저2 >> 노말유저3: 노말유저2님이 퇴장하였습니다.
========= 익명유저 -> 유저1 =========
익명 유저는 메시지를 전송할 수 없습니다.
보기에는 복잡해보이지만 각각 따로 받는다고 생각하면 된다.
'Design Pattern > 행동 패턴(Behavioral patterns)' 카테고리의 다른 글
Observer Pattern, 옵저버 패턴 (0) | 2024.04.01 |
---|---|
Memento Pattern, 메멘토 패턴 (1) | 2024.03.27 |
Iterator Pattern, 반복자 패턴 (0) | 2024.02.28 |
Command Pattern, 커맨드 패턴 (1) | 2024.02.27 |
Chain of Responsibility Pattern, 책임 연쇄 패턴 (0) | 2024.02.27 |