맨위로가기

중재자 패턴

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

1. 개요

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

더 읽어볼만한 페이지

  • 소프트웨어 디자인 패턴 - 모델-뷰-컨트롤러
    모델-뷰-컨트롤러(MVC)는 소프트웨어 디자인 패턴으로, 응용 프로그램을 모델, 뷰, 컨트롤러 세 가지 요소로 분리하여 개발하며, 사용자 인터페이스 개발에서 데이터, 표현 방식, 사용자 입력 처리를 분리해 유지보수성과 확장성을 높이는 데 기여한다.
  • 소프트웨어 디자인 패턴 - 스케줄링 (컴퓨팅)
    스케줄링은 운영 체제가 시스템의 목적과 환경에 맞춰 작업을 관리하는 기법으로, 장기, 중기, 단기 스케줄러를 통해 프로세스를 선택하며, CPU 사용률, 처리량 등을 기준으로 평가하고, FCFS, SJF, RR 등의 알고리즘을 사용한다.
중재자 패턴
개요
이름중재자 패턴
분류행위 디자인 패턴
목적객체 간의 직접적인 상호작용을 줄여 결합도를 낮추고, 중앙 집중적인 중재자 객체를 통해 상호작용을 관리한다.
동기시스템 내의 객체들이 복잡하게 얽혀 있어 상호작용이 어렵고 유지보수가 힘들 때, 중재자 패턴을 사용하여 객체 간의 결합도를 낮추고 상호작용을 단순화한다.
적용 가능성객체 간의 복잡한 상호작용을 캡슐화하고 싶을 때
객체 간의 결합도를 낮추고 독립성을 높이고 싶을 때
객체 간의 상호작용 방식을 중앙에서 제어하고 싶을 때
구조중재자(Mediator) 인터페이스 또는 추상 클래스와 구체적인 중재자(Concrete Mediator) 클래스, 그리고 동료(Colleague) 인터페이스 또는 추상 클래스와 구체적인 동료(Concrete Colleague) 클래스로 구성된다.
참여자Mediator (중재자): 동료 객체 간의 통신을 위한 인터페이스를 정의한다.
ConcreteMediator (구체적인 중재자): Mediator 인터페이스를 구현하고, 동료 객체 간의 통신을 조정한다.
Colleague (동료): 중재자와 통신하는 객체를 정의한다.
ConcreteColleague (구체적인 동료): Colleague 인터페이스를 구현하고, 중재자를 통해 다른 동료와 통신한다.
협력 방법동료 객체는 직접 통신하지 않고, 중재자를 통해 간접적으로 통신한다. 중재자는 동료 객체로부터 요청을 받아 다른 동료 객체에게 전달한다.
결과객체 간의 결합도를 낮출 수 있다.
객체 간의 상호작용 방식을 중앙에서 제어할 수 있다.
시스템의 복잡도를 줄이고 유지보수성을 향상시킬 수 있다.
단점중재자 객체에 복잡성이 집중될 수 있다.
중재자 객체가 시스템의 병목 지점이 될 수 있다.
구현중재자 인터페이스 또는 추상 클래스를 정의하고, 구체적인 중재자 클래스를 구현한다. 동료 인터페이스 또는 추상 클래스를 정의하고, 구체적인 동료 클래스를 구현한다. 중재자 객체는 동료 객체를 참조하고, 동료 객체는 중재자 객체를 참조한다.
관련 패턴퍼사드 패턴, 옵저버 패턴
일본어
패턴 이름 (일본어)Mediatorパターン
패턴 이름 (로마자)Mediētā pātan
목적 (일본어)相互作用するオブジェクトの集合に対して、 相互作用をカプセル化するオブジェクトを定義する。Mediator は、オブジェクトがお互いを明示的に参照し合うことを防ぐことで結合度を低く保ち、オブジェクト間の相互作用を独立に変更できるようにする。
적용 가능성 (일본어)オブジェクト間の相互作用が複雑で、オブジェクトがお互いを明示的に参照し合っている場合。
オブジェクト間の相互作用を集中化し、再利用性とカスタマイズ性を高めたい場合。
オブジェクト間の相互作用のプロトコル(手順)を抽象化したい場合。
구조 (일본어)Mediator (仲介者): Colleague オブジェクト間の通信を調整するためのインターフェースを定義する。
ConcreteMediator (具体的な仲介者): Mediator インターフェースを実装し、Colleague オブジェクト間の通信を調整する。ConcreteMediator は、Colleague オブジェクトへの参照を維持し、Colleague オブジェクトからの要求を処理する。
Colleague (同僚): Mediator オブジェクトと通信するオブジェクトを定義する。
ConcreteColleague (具体的な同僚): Colleague インターフェースを実装し、Mediator オブジェクトを通じて他の Colleague オブジェクトと通信する。
참가자 (일본어)상동 (위에 기술된 내용과 동일)
협력 방법 (일본어)상동 (위에 기술된 내용과 동일)
결과 (일본어)상동 (위에 기술된 내용과 동일)
구현 (일본어)상동 (위에 기술된 내용과 동일)
사용 예 (일본어)GUI 프레임워크에서의 대화 상자 관리, 채팅 애플리케이션에서의 메시지 전송 등

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