一文读懂中介模式

设计二十三式之中介模式

01 意图

中介者是一种行为设计模式,可让您减少对象之间的混乱依赖关系。该模式限制了对象之间的直接通信,并迫使它们仅通过中介对象进行协作。

02 问题

假设您有一个用于创建和编辑客户资料的对话框。它由各种表单控件组成,例如文本字段、复选框、按钮等。

一些表单元素可能会与其他元素交互。例如,选择“我有一只狗”复选框可能会显示一个隐藏的文本字段,用于输入狗的名字。另一个例子是提交按钮,它必须在保存数据之前验证所有字段的值。

通过直接在表单元素的代码中实现此逻辑,您可以使这些元素的类更难在应用程序的其他形式中重用。例如,您将无法在另一个表单中使用该复选框类,因为它与狗的文本字段耦合。您可以使用渲染配置文件表单所涉及的所有类,或者根本不使用。

03 解决方案

中介者模式建议您应该停止要使彼此独立的组件之间的所有直接通信。相反,这些组件必须通过调用将调用重定向到适当组件的特殊中介对象来间接协作。因此,这些组件只依赖于一个中介类,而不是耦合到他们的几十个同事。

在我们的配置文件编辑表单示例中,对话框类本身可以充当中介。很可能,对话框类已经知道它的所有子元素,因此您甚至不需要在此类中引入新的依赖项。

最显着的变化发生在实际的表单元素上。让我们考虑提交按钮。以前,每次用户单击按钮时,都必须验证所有单个表单元素的值。现在它的唯一工作是通知对话框有关点击。收到此通知后,对话框本身会执行验证或将任务传递给各个元素。因此,按钮不再依赖于十几个表单元素,而是仅依赖于对话框类。

通过提取所有类型对话框的通用接口,您可以更进一步,使依赖关系更加松散。该接口将声明所有表单元素都可以用来通知对话框有关这些元素发生的事件的通知方法。因此,我们的提交按钮现在应该能够与实现该接口的任何对话框一起使用。

通过这种方式,中介者模式允许您将各种对象之间的复杂关系网络封装在单个中介者对象中。一个类的依赖项越少,修改、扩展或重用该类就越容易。

04 举个栗子

接近或离开机场控制区的飞机飞行员彼此之间不直接通信。取而代之的是,他们与一名空中交通管制员交谈,该空中交通管制员坐在飞机跑道附近的一座高塔中。如果没有空中交通管制员,飞行员将需要了解机场附近的每架飞机,并与其他数十名飞行员组成的委员会讨论着陆优先事项。这可能会使飞机失事统计数据飙升。

塔不需要控制整个飞行。它的存在只是为了在终端区域实施限制,因为那里涉及的参与者数量可能对飞行员来说是压倒性的。

05 结构实现

在本例中,中介者模式帮助您消除各种 UI 类之间的相互依赖关系:按钮、复选框和文本标签。

由用户触发的元素不会直接与其他元素通信,即使看起来应该如此。相反,该元素只需要让其中介者知道该事件,将任何上下文信息与该通知一起传递。

在此示例中,整个身份验证对话框充当中介。它知道具体元素应该如何协作并促进它们的间接交流。收到有关事件的通知后,对话框决定哪个元素应该处理该事件并相应地重定向调用。

// The mediator interface declares a method used by components
// to notify the mediator about various events. The mediator may
// react to these events and pass the execution to other
// components.
interface Mediator is
    method notify(sender: Component, event: string)


// The concrete mediator class. The intertwined web of
// connections between individual components has been untangled
// and moved into the mediator.
class AuthenticationDialog implements Mediator is
    private field title: string
    private field loginOrRegisterChkBx: Checkbox
    private field loginUsername, loginPassword: Textbox
    private field registrationUsername, registrationPassword,
                  registrationEmail: Textbox
    private field okBtn, cancelBtn: Button

    constructor AuthenticationDialog() is
        // Create all component objects and pass the current
        // mediator into their constructors to establish links.

    // When something happens with a component, it notifies the
    // mediator. Upon receiving a notification, the mediator may
    // do something on its own or pass the request to another
    // component.
    method notify(sender, event) is
        if (sender == loginOrRegisterChkBx and event == "check")
            if (loginOrRegisterChkBx.checked)
                title = "Log in"
                // 1. Show login form components.
                // 2. Hide registration form components.
            else
                title = "Register"
                // 1. Show registration form components.
                // 2. Hide login form components

        if (sender == okBtn && event == "click")
            if (loginOrRegister.checked)
                // Try to find a user using login credentials.
                if (!found)
                    // Show an error message above the login
                    // field.
            else
                // 1. Create a user account using data from the
                // registration fields.
                // 2. Log that user in.
                // ...


// Components communicate with a mediator using the mediator
// interface. Thanks to that, you can use the same components in
// other contexts by linking them with different mediator
// objects.
class Component is
    field dialog: Mediator

    constructor Component(dialog) is
        this.dialog = dialog

    method click() is
        dialog.notify(this, "click")

    method keypress() is
        dialog.notify(this, "keypress")

// Concrete components don't talk to each other. They have only
// one communication channel, which is sending notifications to
// the mediator.
class Button extends Component is
    // ...

class Textbox extends Component is
    // ...

class Checkbox extends Component is
    method check() is
        dialog.notify(this, "check")
    // ...

06 适用场景

  • 当某些类与许多其他类紧密耦合而难以更改时,请使用中介者模式。

    该模式允许您将类之间的所有关系提取到一个单独的类中,从而将特定组件的任何更改与其余组件隔离开来。
    
  • 当您无法在不同的程序中重用组件时使用该模式,因为它过于依赖其他组件。

    应用调解器后,各个组件将不知道其他组件。他们仍然可以通过中介对象相互通信,尽管是间接的。要在不同的应用程序中重用组件,您需要为其提供一个新的中介类。
    
  • 当您发现自己创建大量组件子类只是为了在各种上下文中重用一些基本行为时,请使用调解器。

    由于组件之间的所有关系都包含在中介中,因此很容易通过引入新的中介类来为这些组件定义全新的协作方式,而无需更改组件本身。
    

07 如何实施

  1. 确定一组紧密耦合的类,这些类将受益于更加独立(例如,为了更容易维护或更简单地重用这些类)。

  2. 声明调解器接口并描述调解器和各种组件之间所需的通信协议。在大多数情况下,从组件接收通知的单一方法就足够了。

    当您想在不同的上下文中重用组件类时,此接口至关重要。只要组件通过通用接口与其调解器一起工作,您就可以将组件与调解器的不同实现链接。

  3. 实现具体的中介类。此类将受益于存储对其管理的所有组件的引用。

  4. 您可以更进一步,让中介者负责组件对象的创建和销毁。在此之后,中介可能类似于工厂或门面。

  5. 组件应该存储对中介对象的引用。连接通常在组件的构造函数中建立,其中中介对象作为参数传递。

  6. 更改组件的代码,以便它们调用中介的通知方法,而不是其他组件上的方法。将涉及调用其他组件的代码提取到中介类中。每当中介收到来自该组件的通知时,执行此代码。

08 如何实施


欢迎查看公众号内容:
https://mp.weixin.qq.com/s?__biz=Mzk0NjI5NzE1Ng==&mid=2247483738&idx=1&sn=f35ed728808886aa5a16366feb87eab8&chksm=c3090171f47e8867e67c0da2b7019bff861d92ffe67a49042e5cd5696bbdd0edbd3d803db7c8&token=787431505&lang=zh_CN#rd

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容