中介者与观察者的面向对象设计模式

Posted

技术标签:

【中文标题】中介者与观察者的面向对象设计模式【英文标题】:Mediator Vs Observer Object-Oriented Design Patterns 【发布时间】:2012-03-02 20:17:31 【问题描述】:

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

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

有人可以通过一些明确区分两者的好例子来帮助我区分两者吗?

【问题讨论】:

我将此问题迁移到Programmers.StackExchange 的请求被拒绝,但我在那里提出了similar post,因为我对答案感兴趣。你可能会发现一些有趣的答案。 :) javascript 例子可以看my answer to a similar question。 原始 GoF 书籍在实施部分第 8 点下通过给出使用 MediatorObserver 模式的 ChangeManager 示例来解决该问题。看; paginas.fe.up.pt/~aaguiar/as/gof/hires/pat5g.htm#samplecode 【参考方案1】:

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

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

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

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

希望清楚。

【讨论】:

【参考方案2】:

举个例子:假设您要构建两个应用程序:

    聊天应用程序。 紧急救护车操作员申请。

调解员

构建聊天应用程序时,您将选择mediator 设计模式。

这些人可能会在任何给定时间加入和离开聊天,因此在两个聊天的人之间保持直接引用没有任何意义。 我们仍然需要促进两个人之间的沟通并允许他们聊天。

为什么我们更喜欢mediator?看看它的定义:

使用中介者模式,对象之间的通信是 封装在中介对象中。对象不再通信 直接相互交流,而是通过 调解人。这减少了通信对象之间的依赖关系, 从而减少耦合。

魔法是如何运作的?首先,我们将创建聊天中介并让人员对象注册到它,因此它将与每个人有两个方向连接(该人可以使用聊天中介发送消息,因为它可以访问它,而聊天中介将访问person 对象的接收方法,因为他也可以访问它)

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);

区别:

    聊天mediator 在人员对象之间有两种通信方式(发送和接收),而操作员observable 只有一种方式通信(它告诉救护车observer 开车并完成)。 聊天mediator可以让persons对象在他们之间进行交互(即使不是直接通信),救护车observers只注册到运营商observable事件。 每个人对象都有对聊天mediator的引用,并且聊天mediator保持对每个人的引用。其中救护车observer 不保留对操作员observable 的引用,只有操作员observable 保留对每辆救护车observer 的引用。

【讨论】:

最后一点有帮助。中介者和观察者都实现了相同的目标,但是中介者支持双向通信,而观察者仅以一种方式工作。 没错,很高兴它有帮助 当我读到标题时,我的理解完全相反,实际上救护车呼叫中心是一个调解员——他接到一个电话,然后派救护车、警察和消防员去广播电台是的每辆救护车都是一个观察者 - 他们在广播中等待与他们接触的事件在聊天中:服务器是一个调解者,每个客户端 - 观察者听起来对吗? 嘿@Maks,认为救护车是自动的,他们不会通过无线电联系他们的操作员,只会获取位置和驱动器。 嘿@Maks 再次,你的评论给了我根据我的回答写一篇文章的动力:shokrano.medium.com/mediator-vs-observer-19e3b21ef31c 谢谢!【参考方案3】:

这个解释怎么样 Observer和Mediator在技术上是相同的,都是用来为组件通信提供解耦的方式,但用法不同。

虽然obeserver 通知 订阅组件状态变化(例如创建新的数据库记录),mediator 命令 注册组件来做一些与业务逻辑流相关的事情(向用户发送电子邮件以重置密码)。

观察者

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

调解员

需要显式注册才能连接“发布者”和“消费者” 通知处理是特定业务流程的一部分

【讨论】:

【参考方案4】:

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

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

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

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

来自dofactory的中介者模式结构:

Mediator:定义了一个用于同事之间通信的接口。

Colleague:是一个抽象类,定义了Colleagues之间要通信的事件

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

ConcreteColleague:实现通过Mediator接收到的通知操作,该通知操作已经由其他Colleague

生成

一个真实的例子:

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

让我们看看 Mediator 模式是如何融入其中的。

代码sn-p:

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

解释:

    Eagle 首先通过注册事件加入网络。因为 Eagle 是第一个,所以没有通知任何其他同事。 当 Ostrich 添加到网络时,Eagle 会收到通知:现在呈现输出的第 1 行。 Penguin 添加到网络时,EagleOstrich 都已收到通知:现在呈现输出的第 2 行和第 3 行。 当Eagle通过注销事件离开网络时,OstrichPenguin都得到了通知。现在渲染输出的第 4 行和第 5 行。

【讨论】:

【参考方案5】:

观察者

1。没有

Client1:嘿Subject,你什么时候换?

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

Client3:我知道主题发生了变化。

2。与

客户保持沉默。 一段时间后... 主题:亲爱的客户,我变了!

调解员

1。没有

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

2。与

客户 1:嘿TaxiCenter,请带我一辆出租车Client2:嘿TaxiCenter,请带我一辆出租车

【讨论】:

您的中介者示例是工厂模式,而不是中介者模式 @Pmpr 是中介者设计模式。 TaxiCenter 不会创建出租车,它会通过某种方式提供出租车(可能每辆出租车都会等到 TaxiCenter 说轮到你了) @MohammadKarimi TaxiCenter 不会创建出租车。它为客户分配出租车。【参考方案6】:

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

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

当一个类希望允许其他类注册自己并接收事件通知时使用观察者模式,例如。 G。按钮监听器等

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

【讨论】:

【参考方案7】:

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

在很多情况下你会想要使用观察者模式,关键是一个对象不应该知道其他对象正在观察它的状态。

Mediator 更具体一点,它避免让类直接进行通信,而是通过中介。通过允许将通信卸载到仅处理通信的类,这有助于单一职责原则。

一个经典的 Mediator 示例是在 GUI 中,其中幼稚的方法可能会导致按钮单击事件上的代码显示“如果 Foo 面板被禁用并且 Bar 面板有一个标签说“请输入日期”,那么不要调用服务器,否则继续”,使用调解员模式,它可以说“我只是一个按钮,不知道 Foo 面板和 Bar 面板上的标签,所以我会问我的调解员是否现在调用服务器就可以了。”

或者,如果 Mediator 是使用观察者模式实现的,那么按钮会说“嘿,观察者(包括中介者),我的状态发生了变化(有人点击了我)。如果你在乎的话,做点什么”。在我的示例中,这可能比直接引用中介者更有意义,但在许多情况下,使用观察者模式来实现中介者是有意义的,观察者和中介者之间的区别更多的是一种意图,而不是代码本身的区别。

【讨论】:

我正在寻找“单一责任原则”这个词。【参考方案8】:

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

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

来源:dofactory

例子:

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

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

【讨论】:

观察者的解释似乎更接近命令模式而不是观察者模式

以上是关于中介者与观察者的面向对象设计模式的主要内容,如果未能解决你的问题,请参考以下文章

面向对象设计的设计模式:中介者模式

面向对象的设计模式,观察者模式

《面向对象与程序设计》阅读笔记1

面向对象编程思想-中介者模式

观察者面向对象的设计模式只是实现回调的一种形式吗?

中介者模式