桥接模式的定义是 “将抽象和实现解耦,让它们可以独立变化”。一个类存在两个(或多个)独立变化的维度,通过组合的方式可以让这两个(或多个)维度可以独立进行扩展。简单地说,桥接模式主要是处理二维模型的问题。
这里举一个API接口监控告警的例子:根据不同的告警规则,触发不同渠道的不同紧急程度的告警。
也就是说,这是一个二维的模型,可能会有下面的组合:
- 严重程度发邮件
- 严重程度发微信消息
- 严重程度打语音电话
- 紧急程度发邮件
- 紧急程度发微信消息
- 紧急程度打语音电话
- 普通程度发邮件
- 普通程度发微信消息
- 普通程度打语音电话
下面用PHP的代码实现上面的业务场景。
bridge = $bridge;
}
abstract public function msg($content);
public function send($to, $content) {
$content = $this->msg($content);
$this->bridge->send($to, $content);
}
}
/**
* 按照 紧急程度 划分三个类
*/
//严重程度
class Severe extends Msg {
public function msg($content) {
return '[严重] ' . $content;
}
}
//紧急程度
class Urgency extends Msg {
public function msg($content) {
return '[紧急] ' . $content;
}
}
//普通程度
class Normal extends Msg {
public function msg($content) {
return '[普通] ' . $content;
}
}
/**
* 按照 告警类型 划分三个类
*/
interface MessageType {
function send($to, $content);
}
//发送邮件
class Email implements MessageType {
public function send($user, $content) {
echo "[发邮件] 给{$user}: {$content}" . PHP_EOL;
}
}
//发送微信
class Weixin implements MessageType {
public function send($user, $content) {
echo "[发微信消息] 给{$user}: {$content}" . PHP_EOL;
}
}
//打电话
class Phone implements MessageType {
public function send($user, $content) {
echo "[打语音电话] 给{$user}: {$content}" . PHP_EOL;
}
}
//根据业务需求, 客户端任意组合调用
//严重程度发邮件
$client = new Severe(new Email());
$client->send('张三', '服务器异常了1'); //[发邮件] 给张三: [严重] 服务器异常了1
//严重程度发微信消息
$client = new Severe(new Weixin());
$client->send('张四', '服务器异常了2'); //[发微信消息] 给张四: [严重] 服务器异常了2
//严重程度打语音电话
$client = new Severe(new Phone());
$client->send('张五', '服务器异常了3'); //[打语音电话] 给张五: [严重] 服务器异常了3
//紧急程度发邮件
$client = new Urgency(new Email());
$client->send('李三', '刚才接口响应超时了1'); //[发邮件] 给李三: [紧急] 刚才接口响应超时了1
//紧急程度发微信消息
$client = new Urgency(new Weixin());
$client->send('李四', '刚才接口响应超时了2'); //[发微信消息] 给李四: [紧急] 刚才接口响应超时了2
//紧急程度打语音电话
$client = new Urgency(new Phone());
$client->send('李五', '刚才接口响应超时了3'); //[打语音电话] 给李五: [紧急] 刚才接口响应超时了3
//普通程度发邮件
$client = new Normal(new Email());
$client->send('王三', '记得下班打卡1'); //[发邮件] 给王三: [普通] 记得下班打卡1
//普通程度发微信消息
$client = new Normal(new Weixin());
$client->send('王四', '记得下班打卡2'); //[发微信消息] 给王四: [普通] 记得下班打卡2
//普通程度打语音电话
$client = new Normal(new Phone());
$client->send('王五', '记得下班打卡3'); //[打语音电话] 给王五: [普通] 记得下班打卡3
从上面的代码中可以看出,桥接模式主要针对 二维模型。这种组合式的问题常规方法需要A*B种组合,而桥接模式只需要A+B种组合,维度越多,桥接模式越有优势。
如果使用传统方法,可以用下面的代码表示:
function SevereEmail($user, $content) {
echo "[发邮件] 给{$user}: [严重] {$content}";
}
function SevereWeixin($user, $content) {
echo "[发微信消息] 给{$user}: [严重] {$content}";
}
function SeverePhone($user, $content) {
echo "[打语音电话] 给{$user}: [严重] {$content}";
}
function UrgencyEmail($user, $content) {
echo "[发邮件] 给{$user}: [紧急] {$content}";
}
function UrgencyWeixin($user, $content) {
echo "[发微信消息] 给{$user}: [紧急] {$content}";
}
function UrgencyPhone($user, $content) {
echo "[打语音电话] 给{$user}: [紧急] {$content}";
}
function NormalEmail($user, $content) {
echo "[发邮件] 给{$user}: [普通] {$content}";
}
function NormalWeixin($user, $content) {
echo "[发微信消息] 给{$user}: [普通] {$content}";
}
function NormalPhone($user, $content) {
echo "[打语音电话] 给{$user}: [普通] {$content}";
}
假设现在有5个紧急程度,告警消息的种类有6种,如果使用桥接模式只需要定义 5+6=11个类,而使用传统的方法则需要5*6=30个类(或者方法)。
因此,实际开发中如果需要表示二维(或者更多维度)模型,并且每个维度的内容越多,就可以使用桥接模式。但是如果维度很少,或者每个维度的内容不多,则可以选择使用传统的模式。
源代码:https://gitee.com/rxbook/php_design_pattern/blob/master/code12_bridge.php