반응형
의도
- 여러 객체들이 소통하는 방법을 캡슐화하는 패턴
- 한 집합에 속해있는 객체의 상호작용을 캡슐화하는 객체를 정의한다. 객체들이 직접 서로를 참조하지 않도록 하여 객체 사이의 느슨한 결합(loose coupling)을 촉진시키며, 개발자가 객체의 상호작용을 독립적으로 다양화시킬 수 있게 만든다.
장점
- 여러 컴포넌트 간의 결합도를 중재자를 통해서 낮출 수 있다.
- 컴포넌트의 코드를 변경하지 않고 새로운 중재자를 만들어 사용할 수 있다.
- 각각의 컴포넌트 코드를 보다 간결하게 유지할 수 있다.
단점
- 중재자 역할을 하는 클래스의 복잡도와 결합도가 증가한다.
알려진 사용 예
- 자바
- Executor
- ExecutorService
- 스프링
- DispatcherServlet
활용성
- 여러 객체가 잘 정의된 형태이기는 하지만 복잡한 상호작용을 가질 때
- 객체간의 의존성이 구조화되지 않으며, 잘 이해하기 어려울 때
- 한 객체가 다른 객체를 너무 많이 참조하고, 너무 많은 의사소통을 수행해서 그 객체를 재사용하기 힘들 때
- 여러 클래스에 분산된 행동들이 상속 없이 상황에 맞게 수정되어야 할 때
결과
- 서브클래싱을 제한한다.
- 중재자는 다른 객체 사이에 분산된 객체의 행동들을 하나의 객체로 국한한다. 이 행동을 변경하고자 한다면 Mediator 클래스를 상속하는 서브클래스를 만들면 된다.
- Colleague 클래스는 재사용 가능하다.
- Colleague 객체 사이의 종속성을 줄인다.
- 중재자는 행동에 참여하는 객체 간의 소결합을 촉진시킨다.
- Mediator 클래스와 Colleague 클래스 각각을 독립적으로 다양화시킬 수 있고 재사용할 수 있다.
- 객체 프로토콜을 단순화한다.
- 중재자는 다 대 다의 관계를 일 대 다의 관계로 축소시킨다.
- 일 대 다의 관계가 다 대 다 관계보다 이해하기 쉽고 유지보수 및 확장하기 쉽다.
- 객체 간의 협력 방법을 추상화한다.
- 객체 사이의 중재를 독립적인 개념으로 만들고 이를 캡슐화함으로써 사용자는 각 객체의 행동과 상관없이 객체 간 연결 방법에만 집중할 수 있다.
- 결과적으로 시스템에서 객체가 어떻게 동작하는지 명확하게 알 수 있다.
- 통제가 집중화된다.
- 상호작용의 복잡한 모든 것들이 자신의 내부에서만 오가게 한다.
- 중재자 객체는 동료 객체 간의 상호작용에 관련된 프로토콜을 모두 캡슐화하기 때문에 어느 동료 객체보다도 훨씬 복잡해질 수 있다. 따라서 Mediator 클래스 자체의 유지보수가 어려워지는 경우도 발생할 수 있다.
협력 방법
- Colleague는 Mediator에서 요청을 송수신한다. Mediator는 필요한 Colleague 사이에 요청을 전달할 의무가 있다.
구조
- 일반적인 객체 구조
실제 구현 구조
소스코드
//AbstractMediator
public interface FrontDesk {
void getTowers(Guest guest, int numberOfTowers);
String getRoomNumberFor();
}
//ConcreteMediator
public class DefaultFrontDesk implements FrontDesk{
private final CleaningService cleaningService = new CleaningService(this);
private Guest guest;
public void getTowers(Guest guest, int numberOfTowers) {
this.guest = guest;
//컴포넌트 간의 결합도를 낮추기 위해서 중재자를 이용한다.
//연관되어 있는 컴포넌트에 누구한테 응답을 줘야하는지 구체적으로 알려준다면 다시 컴포넌트 간의 결합이 생겨서 보내면 안된다.
//따라서 요청을 처리하는데 필요한 만큼의 정보만 전달한다. 호텔 서비스를 제공하기 위해서는 회원의 아이디를 알면 된다.
this.cleaningService.getTowers(guest.getId(), numberOfTowers); //회원의 방에 타월을 가져다준다.
}
public String getRoomNumberFor() {
return guest.getRoomNumber();
}
}
//Colleague
public class Guest {
private Integer id;
private String roomNumber;
private final FrontDesk frontDesk;
public Guest() {
this.frontDesk = new DefaultFrontDesk();
}
public Guest(FrontDesk frontDesk) {
this.frontDesk = frontDesk;
}
public void getTowers(int numberOfTowers) {
this.frontDesk.getTowers(this, numberOfTowers);
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getRoomNumber() {
return roomNumber;
}
public void setRoomNumber(String roomNumber) {
this.roomNumber = roomNumber;
}
}
//Colleague
public class CleaningService {
//회원 아이디에 해당하는 방에 서비스를 제공할 수 있다.
private final FrontDesk frontDesk;
public CleaningService(FrontDesk frontDesk) {
this.frontDesk = frontDesk;
}
public void getTowers(Integer guestId, int numberOfTowers) {
String roomNumber = this.frontDesk.getRoomNumberFor();
System.out.println("provide " + numberOfTowers + " towers to room " + roomNumber);
}
}
public class Hotel {
public static void main(String[] args) {
Guest guest = new Guest();
guest.setId(1);
guest.setRoomNumber("100");
guest.getTowers(3);
Guest guest2 = new Guest();
guest2.setId(5);
guest2.setRoomNumber("2030");
guest2.getTowers(5);
}
}
관련 패턴
- 퍼사드 패턴은 객체들로 구성된 서브시스템을 추상화하여 좀 더 편한 인터페이스를 제공하는 것이 목적이기 때문에 중재자 패턴과는 다르다.
- Facade 객체는 서브시스템을 구성하는 객체로만 메시지가 전달되고 그 반대로 서브시스템을 구성하는 객체가 Facade 객체에 메시지 전달은 처리가 되지 않는다.
- Mediator 객체는 Facade와 다르게 양방향으로 메시지 전달이 발생한다.
- 상호 관련된 객체들은 감시자 패턴을 이용해서 중재자 객체들과 교류한다.
[참고자료]
리처드 헬름, 랄프 존슨, 존 블리시디스, 『GoF의 디자인 패턴 : 재사용성을 지닌 객체지향 소프트웨어의 핵심요소』, 김정아 번역, 프로텍미디어(2015)
http://www.cs.unc.edu/~stotts/GOF/hires/pat5efso.htm
반응형