# 중재자 (Mediator) 패턴
# 중재자 패턴이란 무엇인가요?
TIP
여러 객체들이 소통하는 방법을 추상화 및 캡슐화 하는 패턴을 말한다.
여러 컴포넌트간의 결합도를 중재자를 통해 낮출 수 있다. 결합도를 낮추는 이유는 코드를 테스트하기도 재사용하기도, 확장 및 수정하기도 어렵기 때문이다.
- 딱 이모양만 중재자 패턴인 것은 아니다. 여러 방식이 나올 수도 있다.
- Colleague가 Colleague를 참조하고 있지 않는다는 점에 초점을 두어야 한다.
# 어떤 상황에서 중재자 패턴을 적용할까?
호텔과 호텔의 여러 서비스들에 대한 코드가 있다.
public class CleaningService {
public void getTower(Guest guest, int numberOfTower) {
System.out.println(numberOfTower + " towers to " + guest);
}
public void clean(Restaurant restaurant) {
System.out.println("clean " + restaurant);
}
}
호텔의 클리닝 서비스에서는 Guest 방에 타올을 가져다 주는 것과, 레스토랑을 청소하며, 결국 Guest와 Restaurt를 참조하게 된다.
public class Guest {
private Restaurant restaurant = new Restaurant();
private CleaningService cleaningService = new CleaningService();
public void dinner() {
restaurant.dinner(this);
}
public void getTower(int numberOfTower) {
cleaningService.getTower(this, numberOfTower);
}
}
Guest는 타올을 가져다 주는 요청과 식사를 처리하기 위해 CleaningService, Restaurant 를 필드값으로 갖게 된다.
public class Restaurant {
private CleaningService cleaningService = new CleaningService();
public void dinner(Guest guest) {
System.out.println("dinner " + guest);
}
public void clean() {
cleaningService.clean(this);
}
}
Restaurant은 청소를 요청하기 위해 CleaningService를 필드값으로 갖게 된다.
public class Hotel {
public static void main(String[] args) {
Guest guest = new Guest();
guest.getTower(3);
guest.dinner();
Restaurant restaurant = new Restaurant();
restaurant.clean();
}
}
따라서 사용측에서 다음과 같이 사용할 수 있다.
DANGER
결국 위의 클래스들을 보면 서로의 의존관계가 얽히게 된 것을 확인 할 수 있다.
각 클래스가 서로에 대해서 구체적으로 알아햐 하는 상황이며, 특히 Guest가 모든 서비스를 참조하고 있다.
# 중재자 패턴을 적용해보자
public class Guest {
private Integer id;
private FrontDesk frontDesk = new FrontDesk();
public void getTowers(int numberOfTowers) {
this.frontDesk.getTowers(this, numberOfTowers);
}
private void dinner(LocalDateTime dateTime) {
this.frontDesk.dinner(this, dateTime);
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
- Guest는 FrontDesk를 가지고 있는데, CleaningService, Resturant 등 모든 서비스를 중재자인 FrontDesk를 통해 수행한다.
public class CleaningService {
private FrontDesk frontDesk = new FrontDesk();
public void getTowers(Integer guestId, int numberOfTowers) {
String roomNumber = this.frontDesk.getRoomNumberFor(guestId);
System.out.println("provide " + numberOfTowers + " to " + roomNumber);
}
}
public class Restaurant {
public void dinner(Integer id, LocalDateTime dateTime) {
System.out.println("Guest " + id + " want to eat dinner at " + dateTime);
}
}
- Resturant 역시 clean서비스가 필요하다면 직접 CleaningService에게 요청하는 것이 아니라 FrontDesk를 통해 요청하고 있다.
import java.time.LocalDateTime;
public class FrontDesk {
private CleaningService cleaningService = new CleaningService();
private Restaurant restaurant = new Restaurant();
public void getTowers(Guest guest, int numberOfTowers) {
cleaningService.getTowers(guest.getId(), numberOfTowers);
}
public String getRoomNumberFor(Integer guestId) {
return "1111";
}
public void dinner(Guest guest, LocalDateTime dateTime) {
restaurant.dinner(guest.getId(), dateTime);
}
}
- FrontDesk는 중재자로써 모든 서비스들을 알고 있어도 된다. Guest가 요청한 서비스를 각 서비스들에게 전달한다.
# 중재자 패턴의 장단점은 무엇인가요?
장점
- 컴포넌트 코드를 변경하지 않고 새로운 중재자를 만들어 사용할 수 있다. (인터페이스로 만들어야 가능하다. 인터페이스라면 최소한 메서드들이 바뀌지 않는 것을 보장 받을 수 있기 때문이다.)
- 각각의 컴포넌트 코드를 보다 간결하게 유지할 수 있다.
단점
- 중재자 역할을 하는 클래스의 복잡도와 결합도가 증가한다. 의존성이 한 곳으로 몰리기 때문이다.
# 중재자 패턴의 예시는 무엇인가요?
스프링
- 스프링MVC의 DispatcherServlet
# 참고 자료
- https://kingchan223.tistory.com/315
- https://solbins.tistory.com/entry/%EC%8A%A4%ED%81%AC%EB%9E%A9Spring-MVC-%EA%B5%AC%EC%84%B1-%EC%9A%94%EC%86%8C-%EB%B0%8F-%EC%B2%98%EB%A6%AC-%ED%9D%90%EB%A6%84