Mediator Vs Observer面向对象的设计模式

时间:2012-02-10 10:43:37

标签: oop design-patterns observer-pattern mediator

我一直在阅读 Gang Of Four ,以解决我的一些问题,并遇到了 Mediator 模式。

我之前在我的项目中使用 Observer 来制作一些GUI应用程序。我有点困惑,因为我发现两者之间没有太大区别。我浏览找到差异,但找不到任何适合我的查询的答案。

是否有人可以帮我区分两者,并用一些明确划分两者的好例子?

8 个答案:

答案 0 :(得分:95)

观察者模式: 定义对象之间的一对多依赖关系,以便当一个对象更改状态时,将自动通知和更新其所有依赖项。

中介模式: 定义一个封装一组对象如何交互的对象。 Mediator通过使对象明确地相互引用来促进松散耦合,并且它允许您独立地改变它们的交互。

来源:dofactory

示例:

观察者模式: A类可以有零个或多个O型观察者注册它。当A中的某些内容发生变化时,它会通知所有观察者。

中介模式: 你有一些X类实例(或者甚至是几种不同的类型:X,Y和Z),并且它们希望彼此通信(但你不希望每个实例都有相互明确的引用),所以你创建一个中介类M.每个X的实例都有一个M的共享实例的引用,通过它可以与X(或X,Y和Z)的其他实例进行通信。

答案 1 :(得分:32)

在创造Observer和Mediator,设计模式,可重用面向对象软件的元素等术语的原始书中,它说可以使用观察者模式实现Mediator模式。但是,也可以通过让同事(大致相当于Observer模式的主题)引用Mediator类或Mediator接口来实现它。

在许多情况下,您希望使用观察者模式,关键是对象不应该知道其他对象正在观察它的状态。

Mediator更具体一点,它避免让课程直接沟通,而是通过调解员沟通。这有助于单一责任原则,允许将通信卸载到只处理通信的类。

经典的Mediator示例位于GUI中,其中天真的方法可能会导致按钮单击事件上的代码说“如果Foo面板被禁用且Bar面板上有标签说”请输入日期“然后不要调用服务器,否则继续“,在Mediator模式中它可以说”我只是一个按钮,并且没有任何关于Foo面板和Bar面板上的标签的世俗事务,所以我只问我的调解员是否现在调用服务器就行了。“

或者,如果Mediator是使用Observer模式实现的,那么按钮会说“嘿,观察者(包括调解员),我的状态发生了变化(有人点击了我)。如果你关心的话,可以做点什么”。在我的例子中,可能没有意义,然后直接引用中介,但在许多情况下使用Observer模式实现Mediator是有意义的,Observer和Mediator之间的差异将更多的是意图而不是代码本身的差异。

答案 2 :(得分:30)

观察

1。无

  • Client1 :嗨主题,您什么时候更改?

  • Client2 :您何时更改主题?我没有注意到!

  • Client3 :我知道主题已更改。

2。以

  • 客户是沉默的。
  • 一段时间后......
  • 主题:亲爱的客户,我已经改变了!

中保

1。无

  • Client1 :嗨 Taxi1 ,带我去哪里。
  • Client2 :嗨 Taxi1 ,带我去哪里。
  • Client1 :嗨 Taxi2 ,带我去哪里。
  • Client2 :嗨 Taxi2 ,带我去哪里。

2。以

  • Client1 :嗨 TaxiCenter ,请带我出租车
  • Client2 :嗨 TaxiCenter ,请带我出租车

答案 3 :(得分:13)

这些模式用于不同的情况:

当您有两个具有某种依赖关系的子系统并且其中一个子系统需要更改时,使用介体模式,并且由于您可能不想更改依赖于另一个的系统,您可能需要引入一个调解员将解除他们之间的依赖关系。这样,当其中一个子系统发生变化时,您所要做的就是更新中介。

当一个类想要允许其他类自己注册并接收事件通知时,使用观察者模式,例如: G。 ButtonListener等。

这两种模式都允许较少的耦合,但却完全不同。

答案 4 :(得分:5)

尽管它们都用于有组织地讲述状态变化,但它们在结构和语义上都略有不同IMO。

Observer用于从对象本身广播特定对象的状态更改。因此,变化发生在中心对象中,该中心对象也负责发信号。但是,在Mediator中,状态更改可能发生在任何对象中,但它是从调解器广播的。所以流程有所不同。但是,我不认为这会影响我们的代码行为。我们可以使用一个或另一个来实现相同的行为。另一方面,这种差异可能会对代码的概念性理解产生一些影响。

请参阅,使用模式的主要目的是在开发人员之间创建通用语言。因此,当我看到调解员时,我个人理解多个元素试图通过单个代理/集线器进行通信以减少通信噪声(或促进SRP),并且每个对象在具有发信号通知状态变化的能力方面同样重要。例如,想一下接近机场的多架飞机。每个人都应该通过塔架(中介)进行沟通,而不是相互沟通。 (想想1000架飞机在着陆时相互通信 - 这将是一团糟)

但是,当我看到一个观察者时,这意味着我可能会关注一些状态变化,并且应该注册/订阅以监听特定的状态变化。有一个中心对象负责发出状态变化信号。例如,如果我在从A到B的途中关心某个特定的机场,我可以在那个机场注册以捕捉一些广播的事件,比如有一条空跑道或类似的东西。

希望很清楚。

答案 5 :(得分:4)

@cdc极好地解释了意图的差异。

我会在它上面添加更多信息。

Observer:允许将一个对象中的事件通知给不同的对象集(不同类的实例)

Mediator:集中从特定类创建的对象集之间的通信。

来自dofactory的介体模式的结构:

enter image description here

Mediator :定义同事之间进行通信的界面。

同事:是一个抽象类,定义了同事之间要传达的事件

ConcreteMediator :通过协调同事对象实现协作行为并维护其同事

ConcreteColleague :实现通过 Mediator 收到的通知操作,该操作由其他同事 生成

一个真实世界的例子:

您正在网格拓扑中维护计算机网络。如果添加了新计算机或删除了现有计算机,则该网络中的所有其他计算机应该知道这两个事件。

让我们看看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. Ostrich 添加到网络中时,会通知 Eagle :现在呈现输出的第1行。
  3. Penguin 添加到网络时,已通知 Eagle Ostrich :现在呈现输出的第2行和第3行。
  4. Eagle 通过取消注册事件离开网络时,已通知 Ostrich Penguin 。现在渲染输出的第4行和第5行。

答案 6 :(得分:4)

举个例子:考虑要构建两个应用程序:

  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的情况。

答案 7 :(得分:1)

如何解释这个问题 从技术上讲,Observer和Mediator都是相同的,用于为组件通信提供解耦方式,但使用方式不同。

obeserver 通知 订阅的组件有关状态更改(例如,创建新的db记录),mediator 命令 已注册的组件,用于执行与业务逻辑流程相关的操作(向用户发送电子邮件以进行密码重置)。

观察

  • 通知消费者有责任订阅以接收通知
  • 通知处理不是业务流程的一部分

中保

  • 连接&#34;发布商&#34;所需的明确注册和#34;消费者&#34;
  • 通知处理是特定业务流程的一部分