圣诞大减价开始了!

中介

也被称为:中介, 控制器

意图

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

中介设计模式

问题

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

用户界面元素之间的混乱关系

随着应用程序的发展,用户界面元素之间的关系可能变得混乱。

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

UI的元素是相互依赖的

元素可以与其他元素有很多关系。因此,对某些元素的更改可能会影响其他元素。

通过在表单元素的代码中直接实现这个逻辑,你会使得这些元素的类在应用程序的其他表单中更难重用。例如,你将无法在另一个表单中使用复选框类,因为它耦合到狗的文本字段。您可以使用呈现概要文件表单所涉及的所有类,也可以不使用。

解决方案

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

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

UI元素应该通过中介进行通信。

UI元素应该通过中介对象间接地进行通信。

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

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

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

真实的模拟

空中交通管制塔

飞机飞行员在决定谁下一个降落时不会直接与对方交谈。所有通讯都要经过控制塔。

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

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

结构

中介设计模式的结构 中介设计模式的结构
  1. 组件是包含一些业务逻辑的各种类。每个组件都有一个对中介的引用,用中介接口的类型声明。组件并不知道中介的实际类,因此您可以通过将组件链接到不同的中介来在其他程序中重用该组件。

  2. 中介接口声明了与组件通信的方法,通常只包括一个通知方法。组件可以将任何上下文作为此方法的参数传递,包括它们自己的对象,但只能以接收组件和发送方类之间不发生耦合的方式传递。

  3. 混凝土介质封装各个组件之间的关系。具体中介通常保留对它们所管理的所有组件的引用,有时甚至还管理它们的生命周期。

  4. 组件不能知道其他组件。如果组件内部或组件发生了重要的事情,它必须只通知中介。当中介接收到通知时,它可以很容易地识别发送方,这可能足以决定应该触发哪个组件作为返回。

    从组件的角度来看,它看起来完全像一个黑盒。发送方不知道谁将最终处理它的请求,而接收方也不知道是谁首先发送了请求。

伪代码

在本例中,中介pattern可以帮助你消除各种UI类之间的相互依赖:按钮、复选框和文本标签。

中介模式示例的结构

UI对话框类的结构。

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

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

//中介接口声明了组件使用的方法,用于通知中介有关各种事件。中介可以对这些事件做出反应,并将执行传递给其他组件。//具体的中介类。各个组件之间错综复杂的连接网络已经被解开,并转移到中介中。类AuthenticationDialog实现Mediator是私有字段title:字符串私有字段loginOrRegisterChkBx:复选框私有字段loginUsername, loginPassword:文本框私有字段registrationUsername, registrationPassword, registrationEmail:文本框私有字段okBtn, cancelBtn:按钮构造函数AuthenticationDialog() is //通过将当前的//中介传递到它们的构造函数中建立链接来创建所有组件对象。//当组件发生某些事情时,它会通知//中介。在收到通知后,中介可以//自己执行某些操作或将请求传递给另一个//组件。method notify(sender, event) is if (sender == loginOrRegisterChkBx and event == "check") if (loginOrRegisterChkBx.checked) title = "Log in" //显示登录表单组件。/ / 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") // ...

适用性

当由于某些类与许多其他类紧密耦合而难以更改时,请使用Mediator模式。

该模式允许您将类之间的所有关系提取到单独的类中,将对特定组件的任何更改与其余组件隔离开来。

当您不能在不同的程序中重用某个组件(因为它太依赖于其他组件)时,请使用该模式。

应用中介后,单个组件将不知道其他组件。它们仍然可以通过中介对象相互通信,尽管是间接的。为了在不同的应用程序中重用一个组件,您需要为它提供一个新的中介类。

当您发现自己创建了大量的组件子类只是为了在各种上下文中重用一些基本行为时,请使用Mediator。

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

如何实施

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

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

    当您希望在不同的上下文中重用组件类时,这个接口是至关重要的。只要组件通过通用接口与其中介工作,您就可以将组件与中介的不同实现链接起来。

  3. 实现具体的中介类。考虑在中介中存储对所有组件的引用。这样,您就可以从中介的方法中调用任何组件。

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

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

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

利与弊

  • 单一责任原则.您可以将各个组件之间的通信提取到一个地方,使其更容易理解和维护。
  • 打开/关闭原则.您可以引入新的中介,而不必更改实际的组件。
  • 您可以减少程序的各个组件之间的耦合。
  • 您可以更容易地重用单个组件。

与其他模式的关系

  • 责任链命令中介而且观察者处理连接请求发送方和接收方的各种方式:

    • 责任链按顺序将请求沿着潜在接收者的动态链传递,直到其中一个接收者处理它。
    • 命令在发送方和接收方之间建立单向连接。
    • 中介消除发送方和接收方之间的直接连接,迫使它们通过中介对象间接通信。
    • 观察者让接收者动态地订阅和取消订阅接收请求。
  • 外观而且中介有类似的工作:他们试图组织许多紧密耦合的类之间的协作。

    • 外观定义对象子系统的简化接口,但没有引入任何新功能。子系统本身并不知道facade。子系统中的对象可以直接通信。
    • 中介集中系统组件之间的通信。组件只知道中介对象,不直接通信。
  • 两者之间的区别中介而且观察者往往难以捉摸。在大多数情况下,您可以实现这些模式中的任何一种;但有时你可以同时应用这两种方法。我们来看看怎么做。

    的主要目标中介就是消除一组系统组件之间的相互依赖关系。相反,这些组件依赖于单个中介对象。的目标观察者是在对象之间建立动态单向连接,其中一些对象充当其他对象的下属。

    有一个流行的实现中介模式依赖于观察者.中介对象扮演发布者的角色,组件充当订阅和取消订阅中介事件的订阅者。当中介是这样实现的,它可能看起来很相似观察者

    当您感到困惑时,请记住您可以用其他方式实现Mediator模式。例如,您可以永久地将所有组件链接到同一个中介对象。这个实现不像观察者但仍然是Mediator模式的实例。

    现在想象一个程序,其中所有组件都成为发布者,允许彼此之间动态连接。不会有一个集中的中介对象,只有一组分布式的观察者。

代码示例

c#中的中介c++中的中介围棋中的调解人Java中的中介PHP中的中介Python中的中介Ruby中的调解员Rust中的中介Swift中的调解员TypeScript中的中介

Baidu
map