桥接模式:将抽象部分与它的实现部分分离,使它们能够独立地变化
应用桥接模式解决问题的思路:
假设这样一个应用场景,消息发送模块,消息可以分为普通消息-->加急消息-->特急消息,而发送消息的方式可以有站内消息、手机短信、EMail。在不适用设计模式的情况下,我们一般会写一个普通消息类,并封装所有的发送方式在这个类里面,而加急消息和特急消息可以继承自这个普通消息。咋看上去这样做挺好,而且并不复杂,因为无论消息实体和发送方式如何变化,无非就是9种组合而已。但是业务实体和具体实现方法高度耦合在了一起,这样真的好吗?
如果我要扩展新的实现方法,比如加上一个很潮的@给他怎么办?每个业务实体即消息类里,我们都要逐一实现这个@给他的功能。三种消息是写三遍,拿钥匙30种消息怎么办?即使使用了面向接口的方法,通过集成和实现的方式可以少写一些,但是还是没有完全的解耦。
现在我们回头看看桥接模式的定义,把抽象部分和实现部分分离。这样的说法单独来看太难理解了,什么是抽象部分?什么是实现部分?拿上面的业务需求来说,消息本身就是业务的抽象,但是如何实现发送消息,就是具体的实现了。
既然两部分分离了,那抽象部分要想实现功能,必然要持有实现部分的实例。(这句话能理解吧,因为一件业务,被分为两个部分,说白了就如同运输货物这个业务,我把业务实体货物单独抽象拿出来了,至于怎么运输,就看这个单独抽象出来的业务实体持有什么交通工具了,可能是飞机、也可能是轮船,谁知道呢)
我们来看看桥接模式的结构(跟策略模式很像):
Abstraction(Abstraction Class):抽象部分的接口,由于要持有实现部分的实例,所以所以用抽象类来实现。这个对象中的方法,通常都是和具体的业务相关的方法
RefinedAbstraction(class):扩展抽象部分的接口。通常这些对象中,定义跟实际业务相关的方法,这些方法的实现通常会使用Abstraction中定义的方法,也可能需要调用实现部分的对象来完成
Implementor(interface):定义实现部分的接口,这个接口不用和Abstraction中的方法移植,通常是有Implementor接口提供基本的操作。而Abstraction中定义的是基于这些基本操作的业务方法,也就是说Abstraction定义了基于这些基本操作的较高层次的操作
ConcreteImplementor(class):真正实现Implementor接口的对象
/**
*Bridge 桥接模式
*/
/**
*实现发送消息的统一接口
*/
interface SendMessageImplementor
{
/**
*发送消息
*@param message 要发送的消息内容
*@param toUser 消息发送的目标人员
*/
public function send($message, $toUser);
}
/**
*抽象消息类
*/
abstract class AbstractMessage
{
/**
*持有一个实现部分的对象
*/
protected $impl;
/**
*构造方法
*@param impl 实现部分的对象
*/
public function __construct($impl)
{
$this->impl = $impl;
}
/**
*发送消息,转调到具体的实现部分的方法
*@param message 要发送的具体内容
*@param toUser 要发送给的目标人员
*/
public function sendMessage($message, $toUser)
{
$this->impl->send($message, $toUser);
}
}
/**
*以站内消息的方式发送消息
*/
class SendMessageSMS implements SendMessageImplementor
{
public function send($message, $toUser)
{
echo '使用站内消息的方式发送消息:'.$message.'给'.$toUser.'<br/>';
}
}
/**
*以E-Mail的方式发送消息
*/
class SendMessageEmail implements SendMessageImplementor
{
public function send($message, $toUser)
{
echo '使用E-Mail的方式发送消息:'.$message.'给'.$toUser.'<br/>';
}
}
/**
*普通消息的实现
*/
class CommonMessage extends AbstractMessage
{
public function __constrcut($impl)
{
parent::_constrcut($impl);
}
public function sendMessage($message, $toUser)
{
$this->impl->send($message, $toUser);
}
}
/**
*加急消息的实现
*/
class UrgencyMessage extends AbstractMessage
{
public function __constrcut($impl)
{
parent::__constrct($impl);
}
public function sendMessage($message, $toUser)
{
$message .= '<font color=red>加急:'.$message.'</font>';
$this->impl->send($message, $toUser);
}
/**
*扩展自己的新功能,监控消息的处理过程
*@param messagId 被监控的消息的编号
*/
public function watch($messageId)
{
//获取相应的数据,组织成监控的数据对象,然后返回
}
}
//使用
//创建一个用E-Mail发送消息的功能实现
$impl_email = new SendMessageEmail();
//创建一个普通的消息对象,并把$impl绑定给这个实例
$message = new CommonMessage($impl_email);
//发送消息
$message->sendMessage('羊来了,在吃草','牧羊人');
//创建一个加急消息,消息很紧急,不但要发E-Mail,还要发短信告诉这个人
$UrgMessage1= new UrgencyMessage(new SendMessageSMS());
$UrgMessage2 = new UrgencyMessage(new SendMessageEmail());
/**
*这里只是关于设计模式的探讨,在实际应用中,可以为抽象绑定多个业务实现
*$protected $implArray = array()
*在调用send()函数时可以轮训多种实现,这里就不具体去写这种实现方法了
*/
$UrgMessage1->sendMessage('狼来了(短信)',' 牧羊人');
$UrgMessage2->sendMessage('狼来了(邮件)','牧羊人');