맨위로가기

중재자 패턴

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

중재자 패턴은 객체 간의 상호 작용을 캡슐화하여 객체 간 결합도를 낮추는 디자인 패턴이다. 이 패턴은 객체들이 서로 직접 통신하는 대신 중재자 객체를 통해 간접적으로 상호 작용하도록 설계한다. 중재자는 동료 객체 간의 통신 인터페이스를 정의하고, 구체적인 중재자는 이 인터페이스를 구현하여 동료 객체 간의 통신을 조정한다. 중재자 패턴은 객체 간의 상호 작용 방식을 캡슐화하여 느슨한 결합을 촉진하고 상호 작용의 독립적인 변경을 가능하게 한다. 자바와 C#을 포함한 다양한 프로그래밍 언어에서 구현될 수 있으며, GUI, 채팅 시스템 등 다양한 응용 사례에 적용된다.

더 읽어볼만한 페이지

  • 소프트웨어 디자인 패턴 - 모델-뷰-컨트롤러
    모델-뷰-컨트롤러(MVC)는 소프트웨어 디자인 패턴으로, 응용 프로그램을 모델, 뷰, 컨트롤러 세 가지 요소로 분리하여 개발하며, 사용자 인터페이스 개발에서 데이터, 표현 방식, 사용자 입력 처리를 분리해 유지보수성과 확장성을 높이는 데 기여한다.
  • 소프트웨어 디자인 패턴 - 스케줄링 (컴퓨팅)
    스케줄링은 운영 체제가 시스템의 목적과 환경에 맞춰 작업을 관리하는 기법으로, 장기, 중기, 단기 스케줄러를 통해 프로세스를 선택하며, CPU 사용률, 처리량 등을 기준으로 평가하고, FCFS, SJF, RR 등의 알고리즘을 사용한다.

2. 정의

중재자 패턴[1]의 핵심은 "일련의 객체가 상호 작용하는 방식을 캡슐화하는 객체를 정의하는 것"이다. 객체가 서로를 명시적으로 참조하지 않도록 하여 느슨한 결합을 촉진하고, 상호 작용을 독립적으로 변경할 수 있게 한다.[3][4]

객체는 상호 작용을 제어하고 조정하는 중재자 객체를 통해 간접적으로 서로 상호 작용한다. 객체 집합 간의 상호 작용을 캡슐화하는 별도의 (중재자) 객체를 정의하며, 객체는 서로 직접 상호 작용하는 대신 중재자 객체에 상호 작용을 위임한다. 이를 통해 객체는 ''느슨하게 결합''되며, 객체는 중재자 객체만 참조하고 알고 있으며 서로에 대한 명시적인 지식이 없다.

클라이언트 클래스는 중재자를 사용하여 다른 클라이언트에 메시지를 보낼 수 있으며, 중재자 클래스에서 이벤트를 통해 다른 클라이언트로부터 메시지를 받을 수 있다.


  • '''중재자(Mediator):''' ''동료(Colleague)'' 객체 간의 통신 인터페이스를 정의한다.
  • '''구체적인 중재자(ConcreteMediator):''' ''중재자(Mediator)'' 인터페이스를 구현하고, ''동료(Colleague)'' 객체 간의 통신을 조정한다. 모든 ''동료(Colleague)''의 존재와 통신의 목적에 대해 알고 있다.
  • '''구체적인 동료(ConcreteColleague):''' ''중재자(Mediator)''를 통해 다른 ''동료(Colleague)''와 통신한다.

3. 구조

중재자 행위 디자인 패턴


중재자 패턴은 중재자(Mediator)동료(Colleague) 객체들로 구성된다.

  • 중재자(Mediator): 동료(Colleague) 객체 간의 통신 인터페이스를 정의한다.[1]
  • 구체적인 중재자(ConcreteMediator): 중재자(Mediator) 인터페이스를 구현하고, 동료(Colleague) 객체 간의 통신을 조정한다. 모든 동료(Colleague)의 존재와 통신의 목적에 대해 알고 있다.
  • 구체적인 동료(ConcreteColleague): 중재자(Mediator)를 통해 다른 동료(Colleague)와 통신한다.[1]

3. 1. UML 클래스 및 시퀀스 다이어그램

중재자 디자인 패턴을 위한 샘플 UML 클래스와 시퀀스 다이어그램.
[7]]][5]]]

위의 UML 클래스 다이어그램에서 `Colleague1`과 `Colleague2` 클래스는 서로를 직접 참조하거나 업데이트(갱신)하지 않는다. 대신 상호 작용을 제어하고 조율하기 위해 공통 `Mediator` 인터페이스(`mediate()`)를 참조하여 상호 작용 방식에 대해 서로 독립성을 유지한다. `Mediator1` 클래스는 `Colleague1`과 `Colleague2` 간의 상호 작용을 구현한다.

UML 시퀀스 다이어그램은 런타임 상호 작용을 보여준다. 이 예시에서 `Mediator1` 객체는 `Colleague1`과 `Colleague2` 객체 간의 상호 작용을 중재(제어 및 조정)한다.

`Colleague1`이 `Colleague2`와 상호 작용하려는 경우(예: 상태 업데이트/동기화), `Colleague1`은 `Mediator1` 객체에서 `mediate(this)`를 호출하여 `Colleague1`에서 변경된 데이터를 가져와 `Colleague2`에 대해 `action2()`를 수행한다.

그 후 `Colleague2`는 `Mediator1` 객체에서 `mediate(this)`를 호출하여 `Colleague2`에서 변경된 데이터를 가져와 `Colleague1`에 대해 `action1()`을 수행한다.

  • 중재자(Mediator): 동료(Colleague) 객체 간의 통신 인터페이스를 정의한다.
  • 구체적인 중재자(ConcreteMediator): 중재자(Mediator) 인터페이스를 구현하고, 동료(Colleague) 객체 간의 통신을 조정한다. 모든 동료(Colleague)의 존재와 통신의 목적에 대해 알고 있다.
  • 구체적인 동료(ConcreteColleague): 중재자(Mediator)를 통해 다른 동료(Colleague)와 통신한다.

3. 2. 참여자

'''중재자'''[6] - ''동료'' 객체 간의 통신 인터페이스를 정의한다.

'''구체 중재자''' - 중재자 인터페이스를 구현하고 ''동료'' 객체 간의 통신을 조정한다. 모든 ''동료''와 상호 통신과 관련된 해당 목적을 인식한다.

'''동료''' - 해당 ''중재자''를 통해 다른 ''동료''와의 통신 인터페이스를 정의한다.

'''구체 동료''' - 동료 인터페이스를 구현하고 해당 ''중재자''를 통해 다른 ''동료''와 통신한다.[1]

4. 예시

중재자 패턴은 컴포넌트 간의 결합도를 낮추기 위해 사용되며, 컴포넌트들은 서로 직접 통신하지 않고 중재자(Mediator)를 통해 통신한다.

C# 예제에서는 `IComponent` 인터페이스를 정의하고, `Component1`과 `Component2` 클래스가 이를 구현한다. `Mediator` 클래스는 `Component1`과 `Component2`를 등록하고, `ChangeState` 메서드를 통해 이들의 상태를 변경한다.

```csharp

interface IComponent

{

void SetState(object state);

}

class Component1 : IComponent

{

internal void SetState(object state)

{

throw new NotImplementedException();

}

}

class Component2 : IComponent

{

internal void SetState(object state)

{

throw new NotImplementedException();

}

}

// 공통 작업을 중재합니다.

class Mediator

{

internal IComponent Component1 { get; set; }

internal IComponent Component2 { get; set; }

internal void ChangeState(object state)

{

this.Component1.SetState(state);

this.Component2.SetState(state);

}

}

```

채팅방 예제에서는 `Mediator` 클래스가 `MessageReceived` 이벤트를 통해 메시지를 수신하고, `Send` 메서드를 통해 메시지를 전송한다. `Person` 클래스는 `Mediator`에 등록되어 메시지를 주고받는다. 이는 원격 호출과 함께 사용될 때 실용적이다. 하지만 원시 소켓을 사용하면 위임 콜백이 허용되지 않는다.

```csharp

public delegate void MessageReceivedEventHandler(string message, string sender);

public class Mediator

{

public event MessageReceivedEventHandler MessageReceived;

public void Send(string message, string sender)

{

if (MessageReceived != null)

{

Console.WriteLine("Sending '{0}' from {1}", message, sender);

MessageReceived(message, sender);

}

}

}

public class Person

{

private Mediator _mediator;

public string Name { get; set; }

public Person(Mediator mediator, string name)

{

Name = name;

_mediator = mediator;

_mediator.MessageReceived += new MessageReceivedEventHandler(Receive);

}

private void Receive(string message, string sender)

{

if (sender != Name)

Console.WriteLine("{0} received '{1}' from {2}", Name, message, sender);

}

public void Send(string message)

{

_mediator.Send(message, Name);

}

}

```

자바 예제에서는 `Mediator` 객체가 여러 `Storage` 객체의 값을 제어하며, 사용자 코드는 중재자를 통해서만 값에 접근할 수 있다. `Storage` 객체의 값이 변경되면, 옵저버 패턴을 통해 구현된 중재자 객체의 `notifyObservers` 메서드를 호출하여 옵저버들에게 알림을 보낸다.

```java

import java.util.HashMap;

import java.util.Optional;

import java.util.concurrent.CopyOnWriteArrayList;

import java.util.function.Consumer;

class Storage {

T value;

T getValue() {

return value;

}

void setValue(Mediator mediator, String storageName, T value) {

this.value = value;

mediator.notifyObservers(storageName);

}

}

class Mediator {

private final HashMap> storageMap = new HashMap<>();

private final CopyOnWriteArrayList> observers = new CopyOnWriteArrayList<>();

public void setValue(String storageName, T value) {

Storage storage = storageMap.computeIfAbsent(storageName, name -> new Storage<>());

storage.setValue(this, storageName, value);

}

public Optional getValue(String storageName) {

return Optional.ofNullable(storageMap.get(storageName)).map(Storage::getValue);

}

public void addObserver(String storageName, Runnable observer) {

observers.add(eventName -> {

if (eventName.equals(storageName)) {

observer.run();

}

});

}

void notifyObservers(String eventName) {

observers.forEach(observer -> observer.accept(eventName));

}

}

public class MediatorDemo {

public static void main(String[] args) {

Mediator mediator = new Mediator<>();

mediator.setValue("bob", 20);

mediator.setValue("alice", 24);

mediator.getValue("alice").ifPresent(age -> System.out.println("age for alice: " + age));

mediator.addObserver("bob", () -> {

System.out.println("new age for bob: " + mediator.getValue("bob").orElseThrow(RuntimeException::new));

});

mediator.setValue("bob", 21);

}

}

4. 1. 자바

java

import java.awt.Font;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JPanel;

//Colleague 인터페이스

interface Command {

void execute();

}

//Abstract Mediator

interface Mediator {

void book();

void view();

void search();

void registerView(BtnView v);

void registerSearch(BtnSearch s);

void registerBook(BtnBook b);

void registerDisplay(LblDisplay d);

}

//Concrete mediator

class ParticipantMediator implements Mediator {

BtnView btnView;

BtnSearch btnSearch;

BtnBook btnBook;

LblDisplay show;

//....

public void registerView(BtnView v) {

btnView = v;

}

public void registerSearch(BtnSearch s) {

btnSearch = s;

}

public void registerBook(BtnBook b) {

btnBook = b;

}

public void registerDisplay(LblDisplay d) {

show = d;

}

public void book() {

btnBook.setEnabled(false);

btnView.setEnabled(true);

btnSearch.setEnabled(true);

show.setText("booking...");

}

public void view() {

btnView.setEnabled(false);

btnSearch.setEnabled(true);

btnBook.setEnabled(true);

show.setText("viewing...");

}

public void search() {

btnSearch.setEnabled(false);

btnView.setEnabled(true);

btnBook.setEnabled(true);

show.setText("searching...");

}

}

//A concrete colleague

class BtnView extends JButton implements Command {

Mediator med;

BtnView(ActionListener al, Mediator m) {

super("View");

addActionListener(al);

med = m;

med.registerView(this);

}

public void execute() {

med.view();

}

}

//A concrete colleague

class BtnSearch extends JButton implements Command {

Mediator med;

BtnSearch(ActionListener al, Mediator m) {

super("Search");

addActionListener(al);

med = m;

med.registerSearch(this);

}

public void execute() {

med.search();

}

}

//A concrete colleague

class BtnBook extends JButton implements Command {

Mediator med;

BtnBook(ActionListener al, Mediator m) {

super("Book");

addActionListener(al);

med = m;

med.registerBook(this);

}

public void execute() {

med.book();

}

}

class LblDisplay extends JLabel {

Mediator med;

LblDisplay(Mediator m) {

super("Just start...");

med = m;

med.registerDisplay(this);

setFont(new Font("Arial", Font.BOLD, 24));

}

}

class MediatorDemo extends JFrame implements ActionListener {

Mediator med = new ParticipantMediator();

MediatorDemo() {

JPanel p = new JPanel();

p.add(new BtnView(this, med));

p.add(new BtnBook(this, med));

p.add(new BtnSearch(this, med));

getContentPane().add(new LblDisplay(med), "North");

getContentPane().add(p, "South");

setSize(400, 200);

setVisible(true);

}

public void actionPerformed(ActionEvent ae) {

Command comd = (Command) ae.getSource();

comd.execute();

}

public static void main(String[] args) {

new MediatorDemo();

}

}

```

위 코드는 중재자 패턴을 구현한 예제이다. `Mediator` 인터페이스는 `book()`, `view()`, `search()` 메서드를 정의하여 참여자(Colleague) 객체 간의 상호작용을 중재한다. `ParticipantMediator` 클래스는 `Mediator` 인터페이스를 구현하며, `BtnView`, `BtnSearch`, `BtnBook`, `LblDisplay` 객체들을 등록하고 이들 간의 상호작용을 관리한다. 각 버튼(`BtnView`, `BtnSearch`, `BtnBook`)은 `Command` 인터페이스를 구현하여 `execute()` 메서드를 통해 `Mediator`의 해당 메서드를 호출한다.

```java

import java.util.HashMap;

import java.util.Optional;

import java.util.concurrent.CopyOnWriteArrayList;

import java.util.function.Consumer;

class Storage {

T value;

T getValue() {

return value;

}

void setValue(Mediator mediator, String storageName, T value) {

this.value = value;

mediator.notifyObservers(storageName);

}

}

class Mediator {

private final HashMap> storageMap = new HashMap<>();

private final CopyOnWriteArrayList> observers = new CopyOnWriteArrayList<>();

public void setValue(String storageName, T value) {

Storage storage = storageMap.computeIfAbsent(storageName, name -> new Storage<>());

storage.setValue(this, storageName, value);

}

public Optional getValue(String storageName) {

return Optional.ofNullable(storageMap.get(storageName)).map(Storage::getValue);

}

public void addObserver(String storageName, Runnable observer) {

observers.add(eventName -> {

if (eventName.equals(storageName)) {

observer.run();

}

});

}

void notifyObservers(String eventName) {

observers.forEach(observer -> observer.accept(eventName));

}

}

public class MediatorDemo {

public static void main(String[] args) {

Mediator mediator = new Mediator<>();

mediator.setValue("bob", 20);

mediator.setValue("alice", 24);

mediator.getValue("alice").ifPresent(age -> System.out.println("age for alice: " + age));

mediator.addObserver("bob", () -> {

System.out.println("new age for bob: " + mediator.getValue("bob").orElseThrow(RuntimeException::new));

});

mediator.setValue("bob", 21);

}

}

```

이 예제에서 `Mediator` 객체는 여러 `Storage` 객체의 값을 제어한다. 사용자 코드는 중재자를 통해 저장된 값에 접근해야 한다. 저장 객체가 값이 변경되었음을 알리는 이벤트를 발생시키려 할 때, 옵저버 패턴을 사용하여 구현된 중재자 객체의 `notifyObservers` 메서드를 통해 옵저버 목록을 제어한다.

4. 2. C#

중재자 패턴은 컴포넌트가 느슨하게 결합되도록 보장하여 서로를 명시적으로 호출하지 않고 중재자를 통해 호출하도록 한다. 다음은 그 예시이다.

```csharp

interface IComponent

{

void SetState(object state);

}

class Component1 : IComponent

{

internal void SetState(object state)

{

throw new NotImplementedException();

}

}

class Component2 : IComponent

{

internal void SetState(object state)

{

throw new NotImplementedException();

}

}

// 공통 작업을 중재합니다.

class Mediator

{

internal IComponent Component1 { get; set; }

internal IComponent Component2 { get; set; }

internal void ChangeState(object state)

{

this.Component1.SetState(state);

this.Component2.SetState(state);

}

}

```

채팅방은 중재자 패턴을 사용할 수 있다. 많은 '클라이언트'가 다른 클라이언트 중 하나가 작업을 수행할 때마다(채팅방의 경우 각 사람이 메시지를 보낼 때) 각각 메시지를 받는 시스템도 마찬가지이다. 실제로는 채팅방에 중재자 패턴을 사용하는 것은 원격 호출과 함께 사용할 때만 실용적이다. 원시 소켓을 사용하면 위임 콜백 (중재자 클래스의 MessageReceived 이벤트에 가입한 사람들)이 허용되지 않는다.

```csharp

public delegate void MessageReceivedEventHandler(string message, string sender);

public class Mediator

{

public event MessageReceivedEventHandler MessageReceived;

public void Send(string message, string sender)

{

if (MessageReceived != null)

{

Console.WriteLine("Sending '{0}' from {1}", message, sender);

MessageReceived(message, sender);

}

}

}

public class Person

{

private Mediator _mediator;

public string Name { get; set; }

public Person(Mediator mediator, string name)

{

Name = name;

_mediator = mediator;

_mediator.MessageReceived += new MessageReceivedEventHandler(Receive);

}

private void Receive(string message, string sender)

{

if (sender != Name)

Console.WriteLine("{0} received '{1}' from {2}", Name, message, sender);

}

public void Send(string message)

{

_mediator.Send(message, Name);

}

}

5. 응용 사례

java

import java.util.HashMap;

import java.util.Optional;

import java.util.concurrent.CopyOnWriteArrayList;

import java.util.function.Consumer;

class Storage {

T value;

T getValue() {

return value;

}

void setValue(Mediator mediator, String storageName, T value) {

this.value = value;

mediator.notifyObservers(storageName);

}

}

class Mediator {

private final HashMap> storageMap = new HashMap<>();

private final CopyOnWriteArrayList> observers = new CopyOnWriteArrayList<>();

public void setValue(String storageName, T value) {

Storage storage = storageMap.computeIfAbsent(storageName, name -> new Storage<>());

storage.setValue(this, storageName, value);

}

public Optional getValue(String storageName) {

return Optional.ofNullable(storageMap.get(storageName)).map(Storage::getValue);

}

public void addObserver(String storageName, Runnable observer) {

observers.add(eventName -> {

if (eventName.equals(storageName)) {

observer.run();

}

});

}

void notifyObservers(String eventName) {

observers.forEach(observer -> observer.accept(eventName));

}

}

public class MediatorDemo {

public static void main(String[] args) {

Mediator mediator = new Mediator<>();

mediator.setValue("bob", 20);

mediator.setValue("alice", 24);

mediator.getValue("alice").ifPresent(age -> System.out.println("age for alice: " + age));

mediator.addObserver("bob", () -> {

System.out.println("new age for bob: " + mediator.getValue("bob").orElseThrow(RuntimeException::new));

});

mediator.setValue("bob", 21);

}

}

```

위 예제에서 `Mediator` 객체는 여러 `Storage` 객체의 값을 제어하여 사용자 코드가 중재자를 통해 저장된 값에 접근하도록 강제한다. 저장 객체가 값이 변경되었음을 알리는 이벤트를 발생시키려고 할 때, 옵저버 패턴을 사용하여 구현된 중재자 객체가 `notifyObservers` 메서드를 통해 옵저버(관찰자) 목록을 제어한다.

참조

[1] 서적 Design Patterns: Elements of Reusable Object-Oriented Software https://archive.org/[...] Addison Wesley
[2] 웹사이트 The Mediator design pattern - Problem, Solution, and Applicability http://w3sdesign.com[...] 2017-08-12
[3] 서적 Design Patterns Addison-Wesley 1994
[4] 웹사이트 Mediator Design Pattern http://sourcemaking.[...]
[5] 웹사이트 The Mediator design pattern - Structure and Collaboration http://w3sdesign.com[...] 2017-08-12
[6] 서적 Design Patterns: Elements of Reusable Object-Oriented Software Addison Wesley
[7] 웹인용 The Mediator design pattern - Structure and Collaboration http://w3sdesign.com[...] 2017-08-12



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com