Design Pattern_Behavioral Patterns_mediator

박지홍·2026년 5월 1일

DesignPattern

목록 보기
9/12

중재자 패턴

여러 객체들이 소통하는 방법을 캡슐화하는 패턴

예제) 호텔 투숙객이 타월이 필요하면 청소팀에 문의하고, 저녁은 식당에 문의하고, 헬스장 청소도 직접 요청하는 등 이러면 모든 사람이 서로를 다 알아야 하는 불편함이 생김. 프론트 데스크에 말하면 다 해결하도록 함.

before

(모두가 서로를 직접 알고 있음)

Guest.java - 투숙객이 직접 Restaurant와 CleaningService를 갖음

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);  // 청소팀에 직접 말함
    }
}

Restaurant.java — 레스토랑도 CleaningService를 직접 알고 있음, 헬스장도 마찬가지

public class Restaurant {
    private CleaningService cleaningService = new CleaningService(); // 또 직접!

    public void clean() {
        cleaningService.clean(this);  // 청소 요청도 직접
    }
}

CleaningService.java — 청소 서비스는 Gym이랑 Restaurant을 둘 다 알아야 함

public class CleaningService {
    public void clean(Gym gym) { ... }           // Gym 타입 알아야 함
    public void getTower(Guest guest, int n) { ... }  // Guest 타입 알아야 함
    public void clean(Restaurant restaurant) { ... }  // Restaurant 타입도 알아야 함
}

문제점

  1. 새로운 서비스 추가 어려움. 수영장을 추가한다고 하면 연관된 클래스(게스트, 클리닝서비스) 전부 수정해야함.
  2. 클래스 재사용이 안됨. Guset 클래스를 가져다 쓰려면 연관된 클래스가 다 같이 이동해야함.
  3. 의존 관계가 꼬임. 클래스 10개만 되도 추적힘듬.

after - 프론트 데스크 등장

Guest.java - FrontDesk 하나만 알면 됨

public class Guest {
    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);  // 프론트에 요청
    }
}

FrontDesk.java — 핵심 중재자, 모든 서비스를 알고 guest.getId()만 넘겨서 하위 서비스가 Guest 클래스에 의존하지 않게 됨

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";  // DB 조회라고 생각하면 됨
    }

    public void dinner(Guest guest, LocalDateTime dateTime) {
        restaurant.dinner(guest.getId(), dateTime);
    }
}

CleaningService.java - 이제는 guestId만 받고, 방 번호 필요하면 프론트에 물어봄

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);
    }
}

장점과 단점

  • 장점

    • 컴포넌트 코드를 변경하지 않고 새로운 중재자 만들어 사용 가능
    • 각각의 컴포넌트를 보다 간결하게 유지
  • 단점

    • 중재자 역할을 하는 클래스의 복잡도와 결합도가 증가한다.

실제 Spring 예시 - DispatcherServlet

public class MediatorInSpring {
    public static void main(String[] args) {
        DispatcherServlet dispatcherServlet;  // 이게 Mediator!
    }
}
@Controller
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }
}

Spring MVC에서 HTTP 요청이 들어오면 DispatcherServlet이 중재자 역할을 함. Controller가 View를 직접 찾지 않고, DispatcherServlet이 "이 요청은 HelloController한테 보내고, 응답은 hello라는 뷰 템플릿으로 렌더링하고..." 이걸 다 중재함. Controller는 다른 Controller를 모르고, ViewResolver도 직접 부르지 않고, 전부 DispatcherServlet(프론트 데스크)이 관리하는 거야.

0개의 댓글