
Mediator 1. Mediator
소프트웨어 개발에서 객체들이 서로 직접 참조하고 통신하는 방식은 코드의 복잡성을 증가시키고, 시스템의 유지보수성과 유연성을 저하시킵니다. 이러한 문제를 해결하기 위해 등장한 것이 Mediator 패턴입니다. Mediator 패턴은 객체들이 서로 직접 통신하는 대신, 중재자 객체를 통해 상호작용하도록 하여 객체 간의 결합도를 줄이고, 코드의 유연성을 높이는 구조적 디자인 패턴입니다.
이번 글에서는 Mediator 패턴의 개념과 이를 적용할 수 있는 상황, 장단점을 알아보고, 코드 예시를 통해 Mediator 패턴이 어떻게 적용되는지 살펴보겠습니다.
Mediator 패턴이란?
Mediator 패턴은 여러 객체가 서로 직접 통신하는 대신, 중재자 객체(Mediator)를 통해 간접적으로 상호작용하게 만듭니다. 이를 통해 객체 간의 결합도를 낮추고, 상호 의존성을 줄일 수 있습니다. 객체들은 서로를 직접 참조하지 않고, 대신 중재자 객체에 메시지를 보내고 받음으로써 통신을 수행합니다.
Code Smell: Mediator 패턴이 필요한 순간
Mediator 패턴은 다음과 같은 Code Smell을 해결하는 데 매우 유용합니다.
1. 과도한 상호 의존성 (Excessive Interdependencies)
여러 객체가 서로 직접 참조하고, 각 객체가 다른 객체의 메소드를 호출하는 경우 상호 의존성이 과도하게 증가하여 시스템의 유연성이 떨어집니다. Mediator 패턴을 사용하면 이러한 직접적인 참조를 제거하고, 중재자 객체를 통해 간접적인 상호작용을 처리하여 결합도를 낮출 수 있습니다.
2. 복잡한 통신 로직 (Complex Communication Logic)
여러 객체 간에 복잡한 통신 로직이 존재하면, 코드가 난해해지고 유지보수가 힘들어집니다. 각 객체에 흩어진 통신 로직을 중재자 객체로 모아 처리하면 코드가 단순해지고 이해하기 쉬워집니다.
3. 객체 재사용의 어려움 (Difficulty Reusing Objects)
객체들이 강하게 결합되어 있을 경우, 특정 객체를 다른 컨텍스트에서 재사용하기 어려워집니다. Mediator 패턴은 이러한 결합을 줄여 객체 재사용성을 높이는 데 도움을 줍니다.
Mediator 패턴의 구현
아래 코드는 Mediator 패턴을 활용하여 여러 에이전트(Agent)들이 서로 통신하는 간단한 예시입니다. Mediator 클래스는 에이전트들이 보내는 메시지를 중재하여 다른 에이전트에게 전달하는 역할을 합니다.
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public interface IMediator
{
void Send(Entity sender, string message);
}
public class Mediator : IMediator
{
private List<Entity> entities = new List<Entity>();
public void Register(Entity entity)
{
entities.Add(entity);
}
public void Send(Entity sender, string message)
{
foreach (var entity in entities)
{
if (entity != sender)
{
entity.Receive(message);
}
}
}
}
public abstract class Entity
{
protected IMediator mediator;
public Entity(IMediator mediator)
{
this.mediator = mediator;
}
public abstract void Receive(string message);
}
public class Agent : Entity
{
public string Name { get; }
public Agent(IMediator mediator, string name) : base(mediator)
{
this.Name = name;
}
public override void Receive(string message)
{
Console.WriteLine($"{Name} received message: {message}");
}
public void Send(string message)
{
Console.WriteLine($"{Name} sends message: {message}");
mediator.Send(this, message);
}
}
class MediatorOpening
{
void Start()
{
Mediator mediator = new Mediator();
Agent alice = new Agent(mediator, "Alice");
Agent bob = new Agent(mediator, "Bob");
Agent charlie = new Agent(mediator, "Charlie");
mediator.Register(alice);
mediator.Register(bob);
mediator.Register(charlie);
alice.Send("Hello everyone!");
bob.Send("Hi Alice!");
}
}
코드 설명
-
IMediator 인터페이스
Send()
메서드를 통해 메시지를 중재하는 역할을 합니다. 구체적인 중재자는 Mediator 클래스로 구현됩니다. -
Mediator 클래스
에이전트(Agent)들이 등록되면, 각 에이전트가 보내는 메시지를 받아서 다른 에이전트들에게 전달하는 역할을 합니다. 각 에이전트는 Entity 클래스를 상속받습니다. -
Agent 클래스
에이전트는 메시지를 보낼 때 Mediator 객체를 통해 메시지를 전송하고, 다른 에이전트로부터 메시지를 받을 때는Receive()
메서드를 통해 메시지를 출력합니다. -
MediatorOpening 클래스
Mediator 패턴을 사용하는 예시로, 세 명의 에이전트(Alice, Bob, Charlie)가 메시지를 주고받는 모습을 보여줍니다. Alice가 메시지를 보내면 Mediator가 이를 중재해 Bob과 Charlie에게 전달합니다.
Mediator 패턴의 장점
-
객체 간의 결합도 감소
객체들이 서로 직접 참조하지 않고, 중재자를 통해 간접적으로 통신함으로써 결합도가 낮아집니다. 이로 인해 시스템이 더 유연해지고, 객체 간의 상호작용을 변경하거나 확장할 때 코드 수정이 줄어듭니다. -
복잡한 통신 로직 단순화
여러 객체 간의 복잡한 통신 로직을 Mediator 객체에 집중시켜, 통신 로직을 더 단순하고 명확하게 관리할 수 있습니다. -
객체 재사용성 증가
객체들이 서로 독립적으로 동작하기 때문에, 특정 객체를 다른 컨텍스트에서 더 쉽게 재사용할 수 있습니다.
Mediator 패턴의 단점
-
중재자 객체의 복잡성 증가
Mediator 객체가 모든 통신을 처리하게 되면, 중재자 객체 자체가 복잡해질 수 있습니다. 통신 로직이 복잡해질수록 중재자 클래스가 비대해지고 유지보수가 어려워질 수 있습니다. -
의존성 중앙 집중화
모든 상호작용이 Mediator를 통해 이루어지기 때문에, Mediator에 대한 의존성이 높아질 수 있습니다. 이는 중재자가 시스템에서 중요한 역할을 맡게 되어, 중재자 클래스에 문제가 생기면 전체 시스템에 영향을 미칠 수 있습니다.
마무리
Mediator 패턴은 객체 간의 상호 의존성을 줄이고, 통신 로직을 중앙에서 관리함으로써 시스템의 유연성과 유지보수성을 높이는 훌륭한 패턴입니다. 복잡한 객체 간의 통신을 간단하게 만들고, 코드의 결합도를 줄여 객체 재사용성을 높일 수 있다는 장점이 있습니다.
다만, 모든 상호작용을 중재자가 처리하게 되면 중재자 자체가 비대해질 수 있다는 단점도 있으므로, 통신 로직이 복잡해지지 않도록 주의해야 합니다.