# Mediator Pattern
외부 인사가 최근에 코드를 검토한 결과, 현재의 코드에 문제가 많다고 한다. 특히 비어코 위어드(Veerco Wierde)씨는 채팅 애플리케이션에서의 강한 결합(tight coupling)을 지적하고 있다.
이브: 강한 결합이 뭐죠?
페드로: 한 객체가 다른 객체에 대해 너무 많은 것을 알고 있을 때 나타나는 문제예요.
이브: 좀더 구체적으로 설명해 주실 수 있겠어요?
페드로: 현재의 채팅 프로그램 소스를 보시죠.
public class User {
private String name;
List users = new ArrayList();
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 users = new ArrayList();
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?")
페드로: 아주 좋네요.
이브: 여기에 특별한 것은 없어요. 단지 결합도를 줄이는 방법 중의 하나일 뿐이니까요.