Mediator Vs Observer Шаблоны объектно-ориентированного проектирования - PullRequest
83 голосов
/ 10 февраля 2012

Я читал Gang Of Four , чтобы решить некоторые из моих проблем, и наткнулся на шаблон Mediator .

Ранее я использовал Observer в своих проектах для создания приложений с графическим интерфейсом. Я немного сбит с толку, так как не вижу большой разницы между ними. Я просмотрел, чтобы найти разницу, но не смог найти подходящий ответ на мой запрос.

Может ли кто-нибудь помочь мне провести различие между двумя примерами, которые четко разграничивают эти два?

Ответы [ 8 ]

96 голосов
/ 10 февраля 2012

Шаблон Observer: Определяет зависимость «один ко многим» между объектами, чтобы при изменении состояния одного объекта все его иждивенцы уведомлялись и обновлялись автоматически.

Шаблон Посредника: Определите объект, который инкапсулирует, как взаимодействует набор объектов. Посредник способствует слабой связи, не позволяя объектам явно ссылаться друг на друга, и позволяет независимо изменять их взаимодействие.

Источник: Доцент

Пример:

Шаблон наблюдателя: Класс A может иметь ноль или более наблюдателей типа O, зарегистрированных в нем. Когда что-то в А изменяется, оно уведомляет всех наблюдателей.

Шаблон посредника: У вас есть несколько экземпляров класса X (или, может быть, даже несколько разных типов: X, Y и Z), и они хотят общаться друг с другом (но вы не хотите, чтобы у каждого были явные ссылки друг на друга), вы создаете класс-посредник M. Каждый экземпляр X имеет ссылку на общий экземпляр M, через который он может взаимодействовать с другими экземплярами X (или X, Y и Z).

32 голосов
/ 10 февраля 2012

В оригинальной книге, которая ввела термины Наблюдатель и Посредник, Шаблоны проектирования, элементы многократно используемого объектно-ориентированного программного обеспечения , говорится, что образец посредника может быть реализован с использованием образца наблюдателя. Однако это также может быть реализовано с помощью ссылки «Коллеги» (которая примерно эквивалентна «Субъектам шаблона наблюдателя») либо на класс Mediator, либо на интерфейс Mediator.

Есть много случаев, когда вы хотите использовать паттерн наблюдателя, они имеют в виду, что объект не должен знать, какие другие объекты наблюдают его состояние.

Посредник немного более конкретен, он избегает непосредственного общения классов, но вместо этого через посредника. Это помогает принципу единой ответственности, позволяя передавать информацию в класс, который просто обрабатывает коммуникации.

Классический пример Mediator представлен в графическом интерфейсе, где наивный подход может привести к созданию кода для события нажатия кнопки, говорящего «если панель Foo отключена, а на панели« Бар »есть метка с надписью« Пожалуйста, введите дату », тогда не звоните». сервер, иначе идите дальше », где с помощью шаблона« Посредник »он может сказать:« Я просто кнопка, и у меня нет никаких земных дел, зная о панели Foo и метке на панели «Бар», поэтому я просто спрошу своего посредника, если вызов сервера в порядке прямо сейчас. "

Или, если Mediator реализован с использованием паттерна Observer, кнопка скажет: «Эй, наблюдатели (включая посредника), мое состояние изменилось (кто-то щелкнул меня). Сделайте что-нибудь с этим, если вам все равно». В моем примере это, вероятно, имеет меньше смысла, чем прямая ссылка на посредника, но во многих случаях использование шаблона Observer для реализации Mediator имело бы смысл, и разница между Observer и Mediator была бы скорее намерением, чем различием в самом коде.

30 голосов
/ 28 мая 2016

Наблюдатель

1.Без

  • Клиент1 : Эй, Тема , когда вы меняете?

  • Клиент2 : Когда вы изменили Тема ?Я не заметил!

  • Клиент3 : Я знаю, что Тема изменилась.

2.С

  • Клиенты молчат.
  • Некоторое время спустя ...
  • Тема : Уважаемый клиенты , я изменился!

Посредник

1.Без

  • Клиент1 : Эй Такси1 , возьми меня куда-нибудь.
  • Клиент2 : Эй Такси1 , возьми меня где.
  • Клиент1 : Эй Такси2 , возьми меня где-нибудь.
  • Клиент2 : Эй Такси2 , возьми меня куда-нибудь.

2.С

  • Client1 : Эй, TaxiCenter , пожалуйста, возьмите мне Такси .
  • Client2 : Привет, ТаксиЦентр , возьми, пожалуйста, Такси .
13 голосов
/ 10 февраля 2012

Эти шаблоны используются в разных ситуациях:

Шаблон-посредник используется, когда у вас есть две подсистемы с некоторой зависимостью, и одна из них должна быть изменена, и поскольку вы, возможно, не захотите ее менятьВ системе, которая зависит от других, вы можете захотеть ввести посредника, который отделит зависимость между ними.Таким образом, когда одна из подсистем изменяется, все, что вам нужно сделать, это обновить посредник.

Шаблон наблюдателя используется, когда класс хочет позволить другим классам регистрировать себя и получать уведомления о событиях.Например, ButtonListener и т. д.

Оба эти шаблона допускают меньшую связь, но совершенно разные.

5 голосов
/ 05 января 2017

Хотя оба они используются для организованного способа рассказать об изменениях состояния, они немного отличаются структурно и семантически IMO.

Наблюдатель используется для передачи изменения состояния конкретного объекта от самого объекта. Таким образом, изменение происходит в центральном объекте, который также отвечает за его сигнализацию. Однако в Посреднике изменение состояния может происходить в любом объекте, но оно передается от посредника. Так что есть разница в потоке. Но я не думаю, что это влияет на поведение нашего кода. Мы можем использовать то или иное для достижения того же поведения. С другой стороны, это различие может оказать некоторое влияние на концептуальное понимание кода.

Видите ли, основная цель использования шаблонов - это создание общего языка между разработчиками. Итак, когда я вижу посредника, я лично понимаю несколько элементов, пытающихся обмениваться данными через одного посредника / концентратора, чтобы уменьшить шум связи (или продвигать SRP), и каждый объект одинаково важен с точки зрения способности сообщать об изменении состояния. Например, представьте несколько самолетов, приближающихся к аэропорту. Каждый должен общаться через пилон (посредник), а не общаться друг с другом. (Представьте себе, что 1000 самолетов общаются друг с другом при посадке - это было бы беспорядком)

Однако, когда я вижу наблюдателя, это означает, что есть некоторые изменения состояния, о которых я мог бы заботиться и должен зарегистрироваться / подписаться, чтобы прослушивать определенные изменения состояния. Есть центральный объект, ответственный за сигнализацию об изменениях состояния. Например, если я забочусь о конкретном аэропорту на пути из А в В, я могу зарегистрироваться в этом аэропорту, чтобы отследить некоторые трансляции, например, если есть пустая взлетно-посадочная полоса или что-то в этом роде.

Надеюсь, это понятно.

4 голосов
/ 15 июля 2018

Давайте рассмотрим пример: предположим, что вы хотите создать два приложения:

  1. Приложение чата.
  2. Приложение оператора скорой помощи.

Посредник

При создании приложения чата вы будете выбирать шаблон проектирования mediator.

  • Лица могут присоединяться и выходить из чата в любой момент времени, поэтому нет смысла сохранять прямую ссылку между двумя людьми в чате.
  • Нам по-прежнему необходимо облегчить общение между двумя людьми и позволить им поговорить.

Почему мы предпочитаем mediator? просто взгляните на его определение:

При использовании шаблона-посредника связь между объектами инкапсулирован в объекте-посреднике. Объекты больше не общаются непосредственно друг с другом, но вместо этого общаться через посредник. Это уменьшает зависимости между взаимодействующими объектами, тем самым уменьшая сцепление.

Как работает магия? Сначала мы создадим посредник чата и сделаем так, чтобы объекты лиц регистрировались в нем, чтобы у него было двунаправленное соединение с каждым отдельным человеком (человек может отправить сообщение с помощью посредника чата, поскольку у него есть к нему доступ, и посредник чата получит доступ полученный метод объекта человек, потому что он также имеет к нему доступ)

function Person(name) {
    let self = this;
    this._name = name;
    this._chat = null;

    this._receive(from, message) {        
        console.log("{0}: '{1}'".format(from.name(), message));
    }
    this._send(to, message) {
        this._chat.message(this, to, message);
    }
    return {
        receive: (from, message) => { self._receive(from, message) },
        send: (to, message) => { self._send(to, message) },
        initChat: (chat) => { this._chat = chat; },
        name: () => { return this._name; }
    }
}


function ChatMediator() {
    let self = this;
    this._persons = [];    

    return {
        message: function (from, to, message) {
            if (self._persons.indexOf(to) > -1) {
                self._persons[to].receive(from, message);
            }
        },
        register: function (person) {
            person.initChat(self);
            self._persons.push(person);
        }
        unRegister: function (person) {
            person.initChat(null);
            delete self._persons[person.name()];
        }
    }
};

//Usage:
let chat = new ChatMediator();

let colton = new Person('Colton');
let ronan = new Person('Ronan');

chat.register(colton);
chat.register(ronan);

colton.send(colton, 'Hello there, nice to meet you');
ronan.send(ronan, 'Nice to meet you to');

colton.send(colton, 'Goodbye!');
chat.unRegister(colton);

наблюдатель

При создании приложения для вызова 911 вы будете выбирать шаблон проектирования observer.

  • Каждый объект скорой помощи observer хочет получить информацию о чрезвычайном состоянии, чтобы он мог управлять адресом и помогать.
  • Аварийный оператор observable хранит ссылку на каждую машину скорой помощи observers и уведомляет их, когда требуется помощь (или генерируется событие).

Почему мы предпочитаем observer? просто взгляните на его определение:

Объект, называемый субъектом, ведет список своих зависимых вызывать наблюдателей и автоматически уведомлять их о любом состоянии изменения, обычно вызывая один из их методов.

function AmbulanceObserver(name) {
    let self = this;
    this._name = name;
    this._send(address) {
        console.log(this._name + ' has been sent to the address: ' + address);
    }
    return {
        send: (address) => { self._send(address) },
        name: () => { return this._name; }
    }
}


function OperatorObservable() {
    let self = this;
    this._ambulances = [];    

    return {
        send: function (ambulance, address) {
            if (self._ambulances.indexOf(ambulance) > -1) {
                self._ambulances[ambulance].send(address);
            }
        },
        register: function (ambulance) {
            self._ambulances.push(ambulance);
        }
        unRegister: function (ambulance) {
            delete self._ambulances[ambulance.name()];
        }
    }
};

//Usage:
let operator = new OperatorObservable();

let amb111 = new AmbulanceObserver('111');
let amb112 = new AmbulanceObserver('112');

operator.register(amb111);
operator.register(amb112);

operator.send(amb111, '27010 La Sierra Lane Austin, MN 000');
operator.unRegister(amb111);

operator.send(amb112, '97011 La Sierra Lane Austin, BN 111');
operator.unRegister(amb112);

Различия:

  1. В чате mediator имеется двухсторонняя связь между объектами лиц (отправка и получение), при этом оператор observable имеет только одностороннюю связь (он сообщает машине скорой помощи observer о вождении и завершении).
  2. Чат mediator может заставить людей взаимодействовать между собой (даже если это не прямое общение), машины скорой помощи observers регистрируются только у оператора observable событий.
  3. Каждый объект персонажа имеет ссылку на чат mediator, а также в чате mediator хранится ссылка на каждого из людей. Там, где скорая помощь observer не сохраняет ссылки на оператора observable, только оператор observable сохраняет ссылку на каждую скорую помощь observer.
4 голосов
/ 08 августа 2016

@ cdc отлично объяснил разницу в намерениях.

Я добавлю больше информации поверх него.

Наблюдатель : Включает уведомление о событии в одном объекте разному набору объектов (экземплярам разных классов)

Посредник : Централизует связь между множеством объектов, созданных из определенного класса.

Структура структуры Mediator из фабрики :

enter image description here

Посредник : Определяет интерфейс для связи между коллегами.

Коллега : абстрактный класс, определяющий события, которые должны передаваться между коллегами

ConcreteMediator : Реализует кооперативное поведение, координируя Коллега объектов и поддерживает своих коллег

ConcreteColleague : Реализует операции уведомления, полученные через Посредник , который был сгенерирован другим Коллегой

Один пример из реального мира:

Вы поддерживаете сеть компьютеров в топологии Mesh . Если добавляется новый компьютер или удаляется существующий компьютер, все остальные компьютеры в этой сети должны знать об этих двух событиях.

Давайте посмотрим, как шаблон Mediator вписывается в него.

Фрагмент кода:

import java.util.List;
import java.util.ArrayList;

/* Define the contract for communication between Colleagues. 
   Implementation is left to ConcreteMediator */
interface Mediator{
    public void register(Colleague colleague);
    public void unregister(Colleague colleague);
}
/* Define the contract for notification events from Mediator. 
   Implementation is left to ConcreteColleague
*/
abstract class Colleague{
    private Mediator mediator;
    private String name;

    public Colleague(Mediator mediator,String name){
        this.mediator = mediator;
        this.name = name;
    }
    public String toString(){
        return name;
    }
    public abstract void receiveRegisterNotification(Colleague colleague);
    public abstract void receiveUnRegisterNotification(Colleague colleague);    
}
/*  Process notification event raised by other Colleague through Mediator.   
*/
class ComputerColleague extends Colleague {
    private Mediator mediator;

    public ComputerColleague(Mediator mediator,String name){
        super(mediator,name);
    }
    public  void receiveRegisterNotification(Colleague colleague){
        System.out.println("New Computer register event with name:"+colleague+
        ": received @"+this);
        // Send further messages to this new Colleague from now onwards
    }
    public  void receiveUnRegisterNotification(Colleague colleague){
        System.out.println("Computer left unregister event with name:"+colleague+
        ":received @"+this);
        // Do not send further messages to this Colleague from now onwards
    }
}
/* Act as a central hub for communication between different Colleagues. 
   Notifies all Concrete Colleagues on occurrence of an event
*/
class NetworkMediator implements Mediator{
    List<Colleague> colleagues = new ArrayList<Colleague>();

    public NetworkMediator(){

    }

    public void register(Colleague colleague){
        colleagues.add(colleague);
        for (Colleague other : colleagues){
            if ( other != colleague){
                other.receiveRegisterNotification(colleague);
            }
        }
    }
    public void unregister(Colleague colleague){
        colleagues.remove(colleague);
        for (Colleague other : colleagues){
            other.receiveUnRegisterNotification(colleague);
        }
    }
}

public class MediatorPatternDemo{
    public static void main(String args[]){
        Mediator mediator = new NetworkMediator();
        ComputerColleague colleague1 = new ComputerColleague(mediator,"Eagle");
        ComputerColleague colleague2 = new ComputerColleague(mediator,"Ostrich");
        ComputerColleague colleague3 = new ComputerColleague(mediator,"Penguin");
        mediator.register(colleague1);
        mediator.register(colleague2);
        mediator.register(colleague3);
        mediator.unregister(colleague1);
    }
}

выход:

New Computer register event with name:Ostrich: received @Eagle
New Computer register event with name:Penguin: received @Eagle
New Computer register event with name:Penguin: received @Ostrich
Computer left unregister event with name:Eagle:received @Ostrich
Computer left unregister event with name:Eagle:received @Penguin

Пояснение:

  1. Eagle добавляется в сеть сначала через событие регистрации. Нет уведомлений другим коллегам, так как Eagle является первым.
  2. Когда Страус добавляется в сеть, Eagle получает уведомление: теперь выводится строка 1 вывода.
  3. Когда Пингвин добавлен в сеть, оба Eagle и Страус были уведомлены: строка 2 и строка 3 вывода теперь отображаются.
  4. Когда Eagle покинул сеть через событие незарегистрированной регистрации, оба Страус и Пингвин были уведомлены. Строка 4 и строка 5 вывода теперь отображаются.
1 голос
/ 23 мая 2018

Как насчет этого объяснения Технически и Observer, и Mediator одинаковы и используются, чтобы обеспечить разъединенный способ связи между компонентами, но использование отличается.

Хотя obeserver уведомляет подписанные компоненты об изменениях состояния (например, создание новой записи в БД), команды mediator зарегистрированные компоненты для выполнения операций, связанных с бизнес-логикой (отправка электронной почты пользователю для сброса пароля).

Наблюдатель

  • Потребители уведомлений несут ответственность за подписку для получения уведомлений
  • Обработка уведомлений не является частью бизнес-потока

Посредник

  • Явная регистрация необходима для соединения "издатель" и "потребители"
  • Обработка уведомлений является частью определенного бизнес-потока
...