中介者模式的英文翻译是 Mediator Design Pattern。在 GoF 中的《设计模式》一书中,它是这样定义的:
Mediator pattern defines a separate (mediator) object that encapsulates the interaction between a set of objects and the objects delegate their interaction to a mediator object instead of interacting with each other directly.
翻译成中文就是:中介者模式定义了一个单独的(中介)对象,来封装一组对象之间的交互。将这组对象之间的交互委派给与中介对象交互,来避免对象之间的直接交互。
实际上,中介者模式的设计思想跟中间层很像,通过引入中介这个中间层,将一组对象之间的交互关系(或者说依赖关系)从多对多(网状关系)转换为一对多(星状关系)。原来一个对象要跟 n 个对象交互,现在只需要跟一个中介对象交互,从而最小化对象之间的交互关系,降低了代码的复杂度,提高了代码的可读性和可维护性。
中介者模式的结构与实现
结构
- 抽象中介者 (Mediator),抽象中介者角色定义统一的接口用于个同时角色之间的通信。
- 具体中介者(Concrete Mediator)具体中介者角色通过协调各同事角色实现协作行为。为此它要知道并引用各个同事角色。
- 抽象同事类(Colleague),主要负责约束同事对象的类型,并实现一些具体同事类之间的公共功能。
- 具体同事类(Concrete Colleague)每个具体同事类只知道自己的行为,而不了解其他同事的情况,但他们都认识中介者对象。需要与其他同事对象交互时,就通知中介对象,中介对象会负责后续的交互。
代码示例
Mediator.php
<?php
namespace DesignPatterns\Mediator\example1;
abstract class Mediator
{
//定义一个抽象的发送消息方法,得到同事对象和发送信息
abstract public function Send(string $message, Colleague $colleague);
}
Colleague.php
<?php
namespace DesignPatterns\Mediator\example1;
abstract class Colleague
{
protected $mediator;
//构造方法,得到中介者对象
public function __construct(Mediator $mediator)
{
$this->mediator = $mediator;
}
}
ConcreteMediator.php
<?php
namespace DesignPatterns\Mediator\example1;
class ConcreteMediator extends Mediator
{
private $Colleague1;
private $Colleague2;
//需要了解所有的具体同事对象
/**
* @param mixed $colleague1
*/
public function setColleague1($colleague1): void
{
$this->Colleague1 = $colleague1;
}
/**
* @param mixed $colleague2
*/
public function setColleague2($colleague2): void
{
$this->Colleague2 = $colleague2;
}
//重写发送消息的方法,根据对象作出选择的判断,通知对象
public function Send(string $message, Colleague $colleague)
{
if ($this->Colleague1 == $colleague){
$this->Colleague2->Notify($message);
}else{
$this->Colleague1->Notify($message);
}
}
}
ConcreteColleague1.php
<?php
namespace DesignPatterns\Mediator\example1;
class ConcreteColleague1 extends Colleague
{
public function __construct(Mediator $mediator)
{
parent::__construct($mediator);
}
public function Send(string $message)
{
$this->mediator->Send($message,$this);//发送消息时通常是中介者发送出去的
}
public function Notify(string $message)
{
echo ' 同事1得到消息:'.$message."<br>";
}
}
ConcreteColleague2.php
<?php
namespace DesignPatterns\Mediator\example1;
class ConcreteColleague2 extends Colleague
{
public function __construct(Mediator $mediator)
{
parent::__construct($mediator);
}
public function Send(string $message)
{
$this->mediator->Send($message,$this);
}
public function Notify(string $message)
{
echo ' 同事2得到消息:'.$message."<br>";
}
}
Client.php
<?php
namespace DesignPatterns\Mediator\example1;
require dirname(__DIR__).'/../vendor/autoload.php';
class Client
{
public function run()
{
$m = new ConcreteMediator();
//让两个具体同事类认识中介者对象
$c1 = new ConcreteColleague1($m);
$c2 = new ConcreteColleague2($m);
//让中介者认识各个具体同事类对象
$m->setColleague1($c1);
$m->setColleague2($c2);
//具体同事类对象的发送信息都是通过中介者转发
$c1->Send("吃过饭了吗?");
$c2->Send("没有呢,你打算请客吗?");
}
}
$worker = new Client();
$worker->run();
运行结果:
由于有了Mediator,使得ConcreteColleague1和ConcreteColleague2在发送消息和接收消息时其实是通过中介者来完成的,这就减少了它们之间的耦合度。
示例
中介者模式的思想在现实生活中也很常见,比如说交换机。没有交换机存在的时代,每个电话之间都需要电话线连接才能进行通话。如果一个台电话要和其它100台电话通话,那么它就必须要有100条电话线与其它100个电话连接。
后来为了解决这种麻烦,交换机出现了。每个电话只需连入交换机,通话时。只需构建一条电话-交换机-电话的链路,就可以进行通话。所以现在我们的电话理论上可以同世界上任何一台电话通话,但是只需一条电话线。当然现在用电话的人少了,但是手机呀,计算机网络的实现也是在传统通信网的设计上进行演进的。
其实交换机对应的就是中介者模式的中介者,而电话机就是中介者中的同事。下面,就让我们用代码来实现这个思想。
Colleague.php
<?php
namespace DesignPatterns\Mediator\example2;
/**
* 抽象同事类-电话机
* Class Colleague
* @package DesignPatterns\Mediator\example2
*/
abstract class Colleague
{
protected $mediator; //用于存放中介者
abstract public function sendMsg($num,$msg);
abstract public function receiveMsg($msg);
/**
* 设置中介者对象
* @param mixed $mediator
*/
final public function setMediator($mediator): void
{
$this->mediator = $mediator;
}
}
PhoneColleague.php
<?php
namespace DesignPatterns\Mediator\example2;
//具体同事--固话
class PhoneColleague extends Colleague
{
public function sendMsg($num, $msg)
{
echo '固话--发出声音:'.$msg."<br>";
$this->mediator->operation($num,$msg);
}
public function receiveMsg($msg)
{
echo '固话--接收声音:'.$msg."<br>";
}
}
Colleague.php
<?php
namespace DesignPatterns\Mediator\example2;
//具体同事--手机
class TelephoneColleague extends Colleague
{
public function sendMsg($num, $msg)
{
echo '手机--发出声音:'.$msg."<br>";
$this->mediator->operation($num,$msg);
}
public function receiveMsg($msg)
{
echo '手机响铃-------<br>';
echo '手机--接收声音:'.$msg."<br>";
}
}
Mediator.php
<?php
namespace DesignPatterns\Mediator\example2;
/**
* 交换机 中介者角色
* 当只需要一个具体中介者时,Mediator 和 ConcreteMediator可以合二为一
* Class Mediator
* @package DesignPatterns\Mediator\example2
*/
class Mediator
{
protected $colleague =[];
//交换机业务处理
public function operation($num, $message)
{
if (!array_key_exists($num,$this->colleague)) {
echo "交换机内没有此号码信息,无法通话".PHP_EOL;
}else{
$this->colleague[$num]->receiveMsg($message);
}
}
//注册号码
public function register($num,Colleague $colleague)
{
if (!in_array($colleague,$this->colleague)) {
$this->colleague[$num] = $colleague;
}
$colleague->setMediator($this);
}
}
Client.php
<?php
namespace DesignPatterns\Mediator\example2;
require dirname(__DIR__).'/../vendor/autoload.php';
/**
* Class Client
* @package DesignPatterns\Mediator\example2
*/
class Client
{
public function run()
{
//实例化固话
$phone = new PhoneColleague();
$telePhone = new TelephoneColleague();
$mediator = new Mediator();
//注册号码
$mediator->register('6668688',$phone);
$mediator->register('15612341234',$telePhone);
//通话
$phone->sendMsg('15612341234','hello world');
$telePhone->sendMsg('6668688','请讲普通话');
$telePhone->sendMsg('6668688','请讲普通话');
}
}
$worker = new Client();
$worker->run();
运行结果:
中介者模式的优缺点
中介者模式的优点首先是Mediator的出现减少了各个colleague的耦合,使得可以独立的改变和复用各个colleague类和mediator使得任何colleague的改变都不会影响到其他colleague而只与mediator发生变化。其次,由于把对象协作进行了抽象,把中介作为一个独立的概念并将其封装在一个对象中,这样关注的对象从对象各自本身的行为转移到他们之间的交互上来,也就是站在一个更宏观的角度去看待系统。
中介者模式的缺点,首先具体中介者类ConcreteMediator可能会因为ConcreteColleague的越来越多耳边的非常复杂反而不容易维护了。由于ConcreteMediator控制了集中化,于是就把交互复杂性变为了中介者的复杂性,这就使得中介者会变得比任何一个ConcreteColleague都复杂化。
中介模式 VS 观察者模式
我们前面提到,中介模式也是为了解耦对象之间的交互,所有的参与者都只与中介进行交互。而观察者模式中的消息队列,就有点类似中介模式中的“中介”,观察者模式的中观察者和被观察者,就有点类似中介模式中的“参与者”。那问题来了:中介模式和观察者模式的区别在哪里呢?什么时候选择使用中介模式?什么时候选择使用观察者模式呢?
在观察者模式中,尽管一个参与者既可以是观察者,同时也可以是被观察者,但是,大部分情况下,交互关系往往都是单向的,一个参与者要么是观察者,要么是被观察者,不会兼具两种身份。也就是说,在观察者模式的应用场景中,参与者之间的交互关系比较有条理。
而中介模式正好相反。只有当参与者之间的交互关系错综复杂,维护成本很高的时候,我们才考虑使用中介模式。毕竟,中介模式的应用会带来一定的副作用,前面也讲到,它有可能会产生大而复杂的上帝类。除此之外,如果一个参与者状态的改变,其他参与者执行的操作有一定先后顺序的要求,这个时候,中介模式就可以利用中介类,通过先后调用不同参与者的方法,来实现顺序的控制,而观察者模式是无法实现这样的顺序要求的。
总结
中介模式的设计思想跟中间层很像,通过引入中介这个中间层,将一组对象之间的交互关系(或者依赖关系)从多对多(网状关系)转换为一对多(星状关系)。原来一个对象要跟 n 个对象交互,现在只需要跟一个中介对象交互,从而最小化对象之间的交互关系,降低了代码的复杂度,提高了代码的可读性和可维护性。
观察者模式和中介模式都是为了实现参与者之间的解耦,简化交互关系。两者的不同在于应用场景上。在观察者模式的应用场景中,参与者之间的交互比较有条理,一般都是单向的,一个参与者只有一个身份,要么是观察者,要么是被观察者。而在中介模式的应用场景中,参与者之间的交互关系错综复杂,既可以是消息的发送者、也可以同时是消息的接收者。
中介者模式很容易在系统中应用,也很容易在系统中误用,当系统出现‘多对多’交互复杂的对象群时,不要急于使用中介者模式,而要先看看我们的系统在设计上是否合理。
参考资料: 设计模式之美-中介模式:什么时候用中介模式?什么时候用观察者模式?
github示例:https://github.com/yangpanyao/design-patterns/tree/master/Mediator
感觉贵站非常不错,希望与贵站友情链接!
我的网站:建站知道网-http://wozhidaole.com.cn
如果同意互换友情链接,请回复!