중재자 : 객체 간의 직접 통신을 제한하고 중재자 객체를 통해서만 협력하게 함으로써, 객체 간의 혼란스러운 의존 관계를 줄이는 디자인 패턴
위와 같이 회원가입 및 로그인을 수행하는 팝업창이 있다고 해보자.
팝업창 내의 여러 요소들을 서로 상호작용 할 수 있다.
예를 들어, 로그인 버튼을 눌렀는데 실패했다면 "비밀번호가 올바르지 않습니다"와 같은 에러 메시지가 등장할 수 있다.
각각의 요소들은 다른 요소들과 많은 관계를 맺을 수 있다.
이에 따라, 일부 요소를 변경하면 다른 요소들에 영향을 줄 수 있다.
중재자 패턴은 이러한 상황에서 요소 간의 의존 관계를 줄여준다.
즉, 상호작용하는 각각의 객체를 캡슐화함으로써 느슨한 결합을 유지하는 것이다.
예제
사용자를 추가/제거 할 수 있고, 특정 사용자가 다른 모든 사용자에게 메시지를 보내는 시스템을 중재자 패턴을 적용하여 만들어보자!
public interface Mediator
{
void addUser(User user);
void removeUser(User user);
void sendMessage(User user, String message);
}
위와 같이 중재자 인터페이스를 만든다.
public class UserMediator implements Mediator
{
private List<User> userList = new ArrayList<>();
@Override
public void addUser(User user) {
userList.add(user);
}
@Override
public void removeUser(User user) {
userList.remove(user);
}
@Override
public void sendMessage(User sender, String message)
{
for(User receiver : userList)
{
if(sender != receiver)
receiver.receive(message);
}
}
}
위와 같이 중재자 구현 클래스를 만든다.
public abstract class User
{
protected Mediator mediator;
protected String name;
// 생성자
public User(Mediator mediator, String name) {
this.mediator = mediator;
this.name = name;
}
public abstract void send(String message);
public abstract void receive(String message);
}
User 추상 클래스는 위와 같다.
사용자 객체들은 메시지를 주고 받기 위해 서로 통신할 수 있어야 한다.
public class ManUser extends User
{
// 생성자
public ManUser(Mediator mediator, String name) {
super(mediator, name);
}
@Override
public void send(String message)
{
System.out.println("[" + super.name + "] 상남자처럼 메시지 보내기");
super.mediator.sendMessage(this, message);
}
@Override
public void receive(String message)
{
System.out.println("[" + super.name + "] 상남자처럼 메시지 받기: " + message);
}
}
public class WomanUser extends User
{
// 생성자
public WomanUser(Mediator mediator, String name) {
super(mediator, name);
}
@Override
public void send(String message)
{
System.out.println("[" + super.name + "] 상여자처럼 메시지 보내기");
super.mediator.sendMessage(this, message);
}
@Override
public void receive(String message)
{
System.out.println("[" + super.name + "] 상여자처럼 메시지 받기: " + message);
}
}
사용자 추상 클래스를 상속받는 클래스들은 위와 같다.
성별에 따라 클래스를 분리하였다.
사용자 객체들은 중재자를 통해 서로 메시지를 주고받을 수 있다.
public class Client
{
public static void main(String[] args)
{
Mediator mediator = new UserMediator();
User user1 = new ManUser(mediator, "민수");
User user2 = new ManUser(mediator, "재훈");
User user3 = new WomanUser(mediator, "민지");
User user4 = new WomanUser(mediator, "영희");
mediator.addUser(user1);
mediator.addUser(user2);
mediator.addUser(user3);
mediator.addUser(user4);
mediator.removeUser(user4);
user1.send("민수의 메시지 보내기 권법");
user3.send("영희의 메시지 폭격 블래스터");
}
}
위와 같이 중재자가 사용자 객체를 관리하고, 서로 통신할 수 있게 해준다.
각각의 사용자 객체에서 다른 사용자 객체를 참조하고 있지 않지만, 메시지를 보낼 수 있다!
서로를 참조하는 대신, 공통의 중재자를 참조하는 것이다.
사용 시기
- 일부 클래스가 다른 클래스와 단단하게 결합하여 변경하기 어려울 때
- 어떤 컴포넌트가 다른 컴포넌트에 너무 의존해서 재사용할 수 없을 때
- 서로 상호작용하는 객체들을 캡슐화함으로써 느슨한 결합을 유지해야 할 때
- 객체 들 사이의 M : N 관계를 M : 1 관계로 바꿔서 상호작용을 원할하게 해야 할 때
- 객체 간의 의존 관계가 너무 많을 때
장점
- 객체가 서로 통신하기 위해, 서로를 참조하지 않아도 된다.
- 컴포넌트 간의 결합도를 줄일 수 있다.
- 컴포넌트의 재사용성이 올라간다.
- SRP 만족 : 다양한 컴포넌트 간의 통신을 한 곳에서 관리할 수 있다.
- OCP 만족 : 컴포넌트를 변경하지 않고 새로운 중재자를 도입할 수 있다.
단점
- 객체 간의 통신 로직이 복잡해진다.
- 객체의 형태가 자주 변경되는 경우에는 적합하지 않다.
- 중재자의 권한이 너무 커진다.
참고:
'개발' 카테고리의 다른 글
[ 디자인 패턴: 행동 ] (6) 옵저버 (Observer) (0) | 2025.04.12 |
---|---|
[ 디자인 패턴: 행동 ] (5) 메멘토 (Memento, Snapshot) (0) | 2025.04.12 |
[ 디자인 패턴: 행동 ] (3) 반복자 (Iterator) (0) | 2025.04.12 |
[ 디자인 패턴: 행동 ] (2) 커맨드, 명령 (Command) (0) | 2025.04.12 |
[ 디자인 패턴: 행동 ] (1) 책임 연쇄 (Chain of Responsibility, CoR) (0) | 2025.04.12 |