ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Design Pattern] 중재자 패턴 (Mediator Pattern)
    공부/디자인 패턴 2021. 7. 11. 17:00

    중재자 패턴은 클래스 간의 복잡한 관계들을 캡슐화하여 하나의 클래스에서 관리하도록 처리하는 패턴입니다. M:N 관계를 해당 패턴을 사용하면 M:1 관계로 만들어 복잡도를 내리므로 유지 보수 및 확장성에 유리합니다.

    M개의 객체들 사이에 중재자를 추가하여 중재자가 모든 객체들의 통신을 담당하도록 변경하면 중재자 패턴이라 볼 수 있습니다. 이와 같이 진행하면 각 객체들은 서로 알 필요가 없고 중재자 클래스가 관리하므로 느슨한 결합(loose coupling)을 유지할 수 있고 전체적인 흐름을 읽기 편해집니다.

    단점으로는 특정 application에 맞춰져서 개발이 되기 때문에 재사용이 어렵습니다.

    이와 유사한 패턴으로는 퍼사드 패턴(https://brownbears.tistory.com/507)과 옵저버 패턴(https://brownbears.tistory.com/555)이 있습니다.

    클래스 다이어그램

    Mediator: Colleague 객체 간의 상호참조를 위한 인터페이스. 클라이언트 등록, 실행 등의 메소드 정의

    Colleague: 다른 Colleague와의 상호참조를 위한 인터페이스.

    ConcreteMediator: Mediator 구현 클래스. Colleague 간의 상호참조를 조정

    ConcreteColleage: Colleague 구현 클래스. Mediator를 통해 다른 Colleague와의 상호참조

    옵저버 패턴, 퍼사드 패턴과의 차이점

    퍼사드 패턴

    1통신을 위해 인터페이스를 설계하고 제공한다는 점에서 두 패턴은 동일하지만 퍼사드 패턴은 단방향 통신만 가능하지만 중재자 패턴은 양방향 통신을 지원합니다.

    옵저버 패턴

    옵저버 패턴은 1개의 publisher에 대해 N개의 subscriber가 존재하고 observer가 pulling이나 push 방식을 통해 관리하지만 중재자 패턴은 M개의 publisher와 N개의 subscriber 사이에서 1개의 mediator를 통해 통신을 하는 방식입니다.

    예시

    아래는 채팅을 구현한 예시입니다.

    public interface Mediator {
        void addUser(Colleague user);
        void deleteUser(Colleague user);
        void sendMessage(String message, Colleague user);
    
    }
    
    public abstract class Colleague {
        protected Mediator mediator;
        protected String name;
    
        public Colleague(Mediator mediator, String name) {
            this.mediator = mediator;
            this.name = name;
        }
    
        public abstract void send(String msg);
    
        public abstract void receive(String msg);
    }
    
    public class ConcreteMediator implements Mediator {
        private final List<Colleague> users;
    
        public ConcreteMediator() {
            this.users=new ArrayList<>();
        }
    
        @Override
        public void addUser(Colleague user) {
            this.users.add(user);
        }
    
        @Override
        public void deleteUser(Colleague user) {
            this.users.remove(user);
        }
    
        @Override
        public void sendMessage(String message, Colleague user) {
            for(Colleague u : this.users){
                if(u != user){
                    u.receive(message);
                }
            }
        }
    }
    
    public class ConcreteColleague extends Colleague {
        public ConcreteColleague(Mediator mediator, String name) {
            super(mediator, name);
        }
        @Override
        public void send(String msg) {
            System.out.println(this.name+": Sending Message="+msg);
            mediator.sendMessage(msg, this);
        }
    
        @Override
        public void receive(String msg) {
            System.out.println(this.name+": Received Message:"+msg);
        }
    }
    
    Mediator mediator = new ConcreteMediator();
    Colleague user1 = new ConcreteColleague(mediator, "lee");
    Colleague user2 = new ConcreteColleague(mediator, "kim");
    Colleague user3 = new ConcreteColleague(mediator, "park");
    Colleague user4 = new ConcreteColleague(mediator, "yon");
    mediator.addUser(user1);
    mediator.addUser(user2);
    mediator.addUser(user3);
    mediator.addUser(user4);
    
    user1.send("안녕하세요");
    // lee: Sending Message=안녕하세요
    // kim: Received Message:안녕하세요
    // park: Received Message:안녕하세요
    // yon: Received Message:안녕하세요
    
    mediator.deleteUser(user4);
    
    user2.send("yon없지?");
    // kim: Sending Message=yon없지?
    // lee: Received Message:yon없지?
    // park: Received Message:yon없지?

    파이썬

    ABCMeta를 사용해 자바와 동일하게 구현할 수 있습니다.

    from abc import ABCMeta, abstractmethod
    
    class Colleague(metaclass=ABCMeta):
        def __init__(self, _mediator, name):
            self.mediator = _mediator
            self.name = name
    
        @abstractmethod
        def send(self, msg:str):
            pass
    
        @abstractmethod
        def receive(self, msg:str):
            pass
    
    class Mediator(metaclass=ABCMeta):
        @abstractmethod
        def add_users(self, user):
            pass
    
        @abstractmethod
        def delete_user(self, user):
            pass
    
        @abstractmethod
        def send_message(self, msg:str, user):
            pass
    
    class ConcreteMediator(Mediator):
        def __init__(self):
            self.users = []
    
        def add_users(self, *users):
            self.users.extend(users)
    
        def delete_user(self, user):
            self.users.remove(user)
    
        def send_message(self, msg: str, user):
            for _user in self.users:
                if _user == user:
                    continue
                _user.receive(msg)
    
    class ConcreteColleague(Colleague):
        def __init__(self, mediator, name):
            super().__init__(mediator, name)
    
        def send(self, msg: str):
            print(f'{self.name} sending message={msg}')
            self.mediator.send_message(msg, self)
    
        def receive(self, msg: str):
            print(f'{self.name} receiving message={msg}')
    
    mediator = ConcreteMediator()
    user1 = ConcreteColleague(mediator, 'lee')
    user2 = ConcreteColleague(mediator, 'kim')
    user3 = ConcreteColleague(mediator, 'park')
    user4 = ConcreteColleague(mediator, 'yon')
    
    mediator.add_users(user1, user2, user3, user4)
    user1.send("안녕하세요")
    mediator.delete_user(user4)
    user2.send("yon없지?")

    댓글