# 중재자 (Mediator) 패턴

# 중재자 패턴이란 무엇인가요?

TIP

여러 객체들이 소통하는 방법을 추상화 및 캡슐화 하는 패턴을 말한다.

여러 컴포넌트간의 결합도를 중재자를 통해 낮출 수 있다. 결합도를 낮추는 이유는 코드를 테스트하기도 재사용하기도, 확장 및 수정하기도 어렵기 때문이다.

image

  • 딱 이모양만 중재자 패턴인 것은 아니다. 여러 방식이 나올 수도 있다.
  • 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

image

# 참고 자료

  • 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