open:mediator-pattern

Mediator Pattern

외부 인사가 최근에 코드를 검토한 결과, 현재의 코드에 문제가 많다고 한다. 특히 비어코 위어드(Veerco Wierde)씨는 채팅 애플리케이션에서의 강한 결합(tight coupling)을 지적하고 있다.

이브: 강한 결합이 뭐죠?

페드로: 한 객체가 다른 객체에 대해 너무 많은 것을 알고 있을 때 나타나는 문제예요.

이브: 좀더 구체적으로 설명해 주실 수 있겠어요?

페드로: 현재의 채팅 프로그램 소스를 보시죠.

public class User {
  private String name;
  List<User> users = new ArrayList<User>();

  public User(String name) {
    this.name = name;
  }

  public void addUser(User u) {
    users.add(u);
  }

  void sendMessage(String message) {
    String text = String.format("%s: %s\n", name, message);
    for (User u : users) {
      u.receive(text);
    }
  }

  private void receive(String message) {
    // process message
  }
}

페드로: 여기서의 문제는 사용자가 모든 다른 사용자들에 관한 정보를 갖고 있다는 것이죠. 이런 코드를 사용하고 유지/보수 하는 것은 대단히 어렵죠. 새로운 사용자가 이 채팅에 들어올 때마다, 모든 사용자가 addUser 메소드를 통해 그 사용자에 대한 레퍼런스를 추가해야만 하거든요.

이브: 그러면 그 일에 해당하는 부분을 다른 클래스로 옮기면 되지 않나요?

페드로: 어느 정도는 맞아요. 모든 사용자들을 관리하는 중개자(mediator)라고 불리는 클래스를 만들면 돼요. 각 사용자는 이 중개자 객체만을 내부에 담게 되죠.

public class User {
  String name;
  private Mediator m;

  public User(String name, Mediator m) {
    this.name = name;
    this.m = m;
  }

  public void sendMessage(String text) {
    m.sendMessage(this, text);
  }

  public void receive(String text) {
    // process message
  }
}

public class Mediator {

  List<User> users = new ArrayList<User>();

  public void addUser(User u) {
    users.add(u);
  }

  public void sendMessage(User u, String text) {
    for (User user : users) {
      u.receive(text);
    }
  }
}

이브: 이것은 단순한 리팩토링 문제같이 보이는 데요.

페드로: 그럴 수도 있지만, 예를 들어, Ui에서 서로 결합된 수백개의 부품들(components)이 있는 경우에는 이 중개자 패턴이 구세주 역할을 할 수 있어요.

이브: 인정합니다.

페드로: 클로저에서는 이런 경우 어떻게 처리하죠?

이브: 음…​ 보아 하니…​ 중개자라는 것이 하는 일이 사용자들을 저장하고 메시지를 보내는 것이네요.

(def mediator
  (atom {:users []
         :send (fn [users text]
                 (map #(receive % text) users))}))

(defn add-user [u]
  (swap! mediator
         (fn [m]
           (update-in m [:users] conj u))))

(defn send-message [u text]
  (let [send-fn (:send @mediator)
        users (:users @mediator)]
    (send-fn users (format "%s: %s\n" (:name u) text))))

(add-user {:name "Mister White"})
(add-user {:name "Mister Pink"})
(send-message {:name "Joe"} "Toby?")

페드로: 아주 좋네요.

이브: 여기에 특별한 것은 없어요. 단지 결합도를 줄이는 방법 중의 하나일 뿐이니까요.


  • open/mediator-pattern.txt
  • 마지막으로 수정됨: 2021/11/21 12:35
  • 저자 127.0.0.1