중재자 패턴

정선호·2023년 5월 30일
0

Design Patterns

목록 보기
21/24

중재자 패턴

위키피디아 - 중재자 패턴
설명 및 스도코드

  • 객체간 혼란스러운 의존 관계들을 줄일 수 있는 패턴
    • 객체 간의 직접 통신을 제한하고 중재자 객체를 통해서만 협력하도록 한다
    • 이를 통해 통신 객체 간의 결합도를 감소시킨다

중재자 패턴의 구조

  • 컴포넌트(Component)
    • 비즈니스 로직을 포함한 다양한 클래스들
    • 각 컴포넌트에는 중재자에 대한 참조가 있는데, 이 중재자는 중재자 인터페이스 유형으로 선언됨
    • 따라서 컴포넌트를 다른 중재자에 연결해 다른 프로그램에서 재사용할 수 있음
  • 중재자 인터페이스(Mediator Interface)
    • 일반적으로 단일 알람 메서드만을 포함하는 컴포넌트들과의 통신 메서드 선언
    • 컴포넌트들은 자체 객체들을 포함하여 모든 콘텍스트를 이 메서드의 인수로 전달할 수 있지만 이는 수신자 컴포넌트와 발송자 클래스 간의 결합이 발생하지 않는 방식으로만 가능
  • 구상 중재자(Concrete Mediator)
    • 다양한 컴포넌트 간의 관계를 캡슐화
    • 자신이 관리하는 모든 컴포넌트에 대한 참조를 유지하고 때로는 그들의 수명주기를 관리

컴포넌트들은 다른 컴포넌트들을 인식하지 않아야 한다. 컴포넌트 내에서 또는 컴포넌트에 중요한 일이 발생하면, 컴포넌트는 이를 중재자에게만 알려야 한다. 중재자는 알람을 받으면 발송자를 쉽게 식별할 수 있으며, 이는 응답으로 어떤 컴포넌트가 작동되어야 하는지 결정하기에 충분할 수 있다

컴포넌트의 관점에서는 모든 것들이 블랙박스들처럼 보인다. 발송자는 누가 요청을 처리할 지 모르고 수신자는 누가 처음에 요청을 보냈는지 모른다

중재자 패턴의 적용

  • 일부 클래스들이 다른 클래스들과 단단하게 결합되어 변경하기 어려울 때 사용
    • 중재자 패턴 사용시 특정 컴포넌트에 대한 모든 변경을 나머지 컴포넌트들로부터 고립하여 클래스 간의 모든 관계들을 별도의 클래스로 추출할 수 있음
  • 타 컴포넌트들에 너무 의존하기 때문에 다른 프로그램에서 컴포넌트를 재사용할 수 없는 경우에 사용
    • 중재자 패턴을 적용하면, 그 후 개별 컴포넌트들은 다른 컴포넌트들을 인식하지 못한다. 또, 비록 간접적이기는 하지만 컴포넌트들은 중재자 객체를 통해 여전히 서로 통신할 수 있다. 다른 앱에서 컴포넌트를 재사용하려면 그 앱에 새 중재자 클래스를 제공해야 한다.
  • 몇 가지 기본 행동을 다양한 콘텍스트들에서 재사용하기 위해 수많은 컴포넌트 자식 클래스들을 만들고 있는 스스로를 발견했을 때 사용
    • 컴포넌트들 간의 모든 관계들이 중재자 내에 포함되어 있으므로 컴포넌트들 자체를 변경할 필요 없이 새 중재자 클래스들을 도입하여 이러한 컴포넌트들이 협업할 수 있는 완전히 새로운 방법들을 쉽게 정의할 수 있다.
  • 중재자 패턴이 전지전능한 객체로 변하는 것을 조심하자

다른 패턴과의 관계

  • 커맨드, 중재자, 옵서버 및 책임 연쇄 패턴은 요청의 발신자와 수신자를 연결하는 다양한 방법을 다룬다.
    • 책임 연쇄 패턴은 잠재적 수신자의 동적 체인을 따라 수신자 중 하나에 의해 요청이 처리될 때까지 요청을 순차적으로 전달한다.
    • 커맨드 패턴은 발신자와 수신자 간의 단방향 연결을 설립한다.
    • 중재자 패턴은 발신자와 수신자 간의 직접 연결을 제거하여 그들이 중재자 객체를 통해 간접적으로 통신하도록 강제한다.
    • 옵서버 패턴은 수신자들이 요청들의 수신을 동적으로 구독 및 구독 취소할 수 있도록 한다.
  • 중재자와 퍼사드 패턴은 비슷한 역할을 한다. 둘 다 밀접하게 결합된 많은 클래스 간의 협업을 구성하려고 한다.
    • 퍼사드 패턴은 객체들의 하위 시스템에 대한 단순화된 인터페이스를 정의하지만 새로운 기능을 도입하지는 않는다. 하위 시스템 자체는 퍼사드를 인식하지 못하며, 하위 시스템 내의 객체들은 서로 직접 통신할 수 있다.
    • 중재자는 시스템 컴포넌트 간의 통신을 중앙 집중화한다. 컴포넌트들은 중재자 객체에 대해서만 알며 서로 직접 통신하지 않는다.
  • 중재자와 옵서버 패턴의 차이는 종종 애매하다. 대부분의 경우 두 패턴 중 하나를 구현할 수 있으나, 때로는 두 패턴을 동시에 적용할 수 있다.
    • 중재자의 주목적은 시스템 컴포넌트들의 집합 간의 상호 의존성을 제거하는 것이다. 그러면 이러한 컴포넌트들은 대신 단일 중재자 객체에 의존하게 된다. 옵서버 패턴의의 목적은 객체들 사이에 단방향 연결을 설정하는 것으로, 여기서 일부 객체는 다른 객체의 종속자 역할을 한다.
    • 옵서버 패턴에 의존하는 중재자 패턴의 인기 있는 구현이 있다. 중재자 객체는 출판사의 역할을 맡고, 컴포넌트들은 중재자의 이벤트들을 구독 및 구독 취소하는 구독자들의 역할을 맡는다. 중재자가 이러한 방식으로 구현되면 옵서버 패턴과 매우 유사하게 보일 수 있다.

중재자 패턴 예시

  • 컴포넌트 인터페이스
interface ISource{
  public void setMediator(Mediator mediator);
  public void eventOccured(String event);
}

package MediatorPattern;
 
interface IDestination{
  public void receiveEvent(String from, String event);
}
  • 콘크리트 컴포넌트
public class TcpComm implements ISource {
  Mediator mediator;
 
  @Override
  public void setMediator(Mediator mediator){ // 중재자 설정
      this.mediator = mediator;
  }
 
  @Override
  public void eventOccured(String event){ // 이벤트의 전달
      mediator.onEvent("TCP comm", event);
  }
}
 
class Display implements IDestination{
  @Override
  public void receiveEvent(String from, String event){
      System.out.println("Display : from " + from + " event : " + event);
  }
}
  • 중재자 인터페이스
interface IMediator{
  public void addDestination(IDestination destination);
  public void onEvent(String from, String event);
}
  • 콘크리트 중재자
class CMediator implements IMediator{
  List<IDestination> list = new ArrayList<IDestination>();
  
  @Override
  public void addDestination(IDestination destination){
  	list.add(destination);
  }
  @Override
  public void onEvent(String from, String event){
      for(IDestination each : list){ // 이벤트의 전송
          each.receiveEvent(from, event);
      }
   }
}
  • 클라이언트
public class MediatorMain {
  public static void main(String[] args) {
    Mediator mediator = new Mediator();
  
    ISource tcp = new TcpComm();
    tcp.setMediator(mediator);
    ISource system = new SystemSignal();
    system.setMediator(mediator);
  
    mediator.addDestination(new Display());
    mediator.addDestination(new Log());
    tcp.eventOccured("connected");
    tcp.eventOccured("disconnected");
  
    system.eventOccured("key input");
    system.eventOccured("mouse input");
  }
}
profile
학습한 내용을 빠르게 다시 찾기 위한 저장소

0개의 댓글