更多优质内容
请关注公众号

面向对象和设计模式(十六)桥接模式——PHP语言实现-张柏沛IT博客

正文内容

面向对象和设计模式(十六)桥接模式——PHP语言实现

栏目:PHP 系列:面向对象与设计模式 发布时间:2023-02-11 10:39 浏览量:1328
本系列文章目录
展开/收起

一、桥接模式概念

网上对桥接模式的解释有两种。

第一种理解方式是将抽象和实现解耦,让它们可以独立变化。

 

说具体一点就是将抽象和实现完全分开,“抽象”指的并非“抽象类”或“接口”,而是被抽象出来的一套“类库”,它只包含骨架代码负责流程控制和调度,真正的业务逻辑需要委派给定义中的“实现”来完成。而定义中的“实现”也并非“接口的实现类”,而是一套独立的“类库”。“抽象”和“实现”独立开发,通过对象之间的组合关系组装在一起。

 

另一种理解方式是当一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展,而不是将多个维度包含在一个类中,导致继承的子类指数级增长。

 

举个例子,有一个画笔类,它有两个维度的概念:颜色和种类。颜色有:红色、黑色、白色、黄色;种类有:蜡笔、毛笔、铅笔。通过继承的方式来实现这个需求,需要写3*4=12个子类。但如果将颜色作为一个维度的类,将种类作为一个维度的类,只需要实现3+4=7个类,然后通过将这些类组合起来,实现最终的画笔类。

 

接下来我们看看桥接模式包含的角色以及类图。

 

 

抽象化(Abstraction)角色:抽象化角色是个抽象类,提供高层控制逻辑,依赖于一个实现化对象的引用来完成底层实际工作。

扩展抽象化(Refined Abstraction)角色:是抽象化角色的实现。

实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。

具体实现化(Concrete Implementor)角色:实现化角色接口的具体实现。

客户端(Client):仅关心如何与抽象部分合作。但是,客户端需要将抽象对象与一个实现对象连接起来。

一般来说,抽象化角色用来承载主维度,实现化角色用来承载其他维度。就像上面我提到的画笔类的例子,种类这个维度就可以作为抽象化角色,衍化出蜡笔类、毛笔类、铅笔类这3个扩展抽象化类;而颜色这个维度可以作为实现化角色,衍化出红色类、黑色类、白色类、黄色类这4个具体实现类;如果再多扩展一个维度:品牌,那么就可以再定义一个品牌维度的实现化角色类。

然后在每个扩展抽象化类(“种类”类)中引用 颜色类 和 品牌类 的方法。

 

 

二、桥接模式的适用场景

当一个对象有多个变化因素的时候,需要将不同维度的具体实现分离,避免继承带来的组合爆炸。如手机品牌有2种变化因素,一个是品牌,一个是功能。

当控制流程和具体实现需要分开来独立的变化时,可以使用桥接模式。

下面我们来看一个例子,有一个 API 接口监控告警 系统。告警支持多种通知渠道,包括:邮件、短信、微信、自动语音电话。通知的紧急程度有多种类型,包括:SEVERE(严重)、URGENCY(紧急)、NORMAL(普通)、TRIVIAL(无关紧要)。不同的紧急程度对应不同的通知渠道。比如,SERVE(严重)级别的消息会通过“自动语音电话”告知相关人员。

最简单的实现方式如下:

<?php

class NotificationEmergencyLevel {
    const SEVERE = 1;
    const URGENCY = 2;
    const NORMAL = 3;
    const TRIVIAL = 4;
  }
  
  class Notification {
    private $emailAddresses = [];
    private $telephones = [];
    private $wechatIds = [];
  
    public function setEmailAddress($emailAddress) {
      $this->emailAddresses = $emailAddress;
    }
  
    public function setTelephones($telephones) {
      $this->telephones = $telephones;
    }
  
    public function setWechatIds($wechatIds) {
      $this->wechatIds = $wechatIds;
    }
  
    public function notify($level, $message) {
      if ($level == (NotificationEmergencyLevel::SEVERE)) {
        //...自动语音电话
      } else if ($level == NotificationEmergencyLevel::URGENCY) {
        //...发微信
      } else if ($level == NotificationEmergencyLevel::NORMAL) {
        //...发邮件
      } else if ($level == NotificationEmergencyLevel::TRIVIAL) {
        //...发邮件
      }
    }
  }

在这个例子中,变化因素有通知方式和告警级别两个维度,通知方式取决于告警级别。我们可以使用桥接模式对其进行优化,以Notification作为抽象化角色承载告警级别这一维度,以新类MessageSender作为实现化角色承载通知方式这一维度,再将其组合。

<?php

class NotificationEmergencyLevel {
    const SEVERE = 1;
    const URGENCY = 2;
    const NORMAL = 3;
    const TRIVIAL = 4;
  }
  
  abstract class Notification {
      protected $messageSender;

      public function __construct(MessageSender $messageSender)
      {
          $this->messageSender = $messageSender;
      }

      abstract public function notify($message);
  }

  class SevereNotification extends Notification { 
      public function __construct(MessageSender $msgSender) { 
          parent::__construct($msgSender); 
        } 

        public function notify($message) { 
            // ...体现告警等级差异的代码
            $this->messageSender->send($message); 
        }
    }

    class UrgencyNotification extends Notification {  
        // 与SevereNotification代码结构类似,所以省略...
    }
    class NormalNotification extends Notification {  
        // 与SevereNotification代码结构类似,所以省略...
    }
    class TrivialNotification extends Notification {  
        // 与SevereNotification代码结构类似,所以省略...
    }

  interface MessageSender{
      public function send($message);
  }

  class TelephoneMsgSender implements MessageSender{
      private $telephones = [];

      public function setTelephones($telephones) {
        $this->telephones = $telephones;
      }

      public function send($message){
          // ...具体实现
      }
  }

  class EmailMsgSender implements MessageSender{
    // 与TelephoneMsgSender代码结构类似,所以省略...
  }

  class WechatMsgSender implements MessageSender { 
      // 与TelephoneMsgSender代码结构类似,所以省略...
  }

这样一来因告警级别而异的具体实现可以在Notification的子类完成,因通知方式而异的具体实现可以在MessageSender类完成。

 

 

三、桥接模式优缺点

优点

1、分离抽象接口及其实现部分。

2、桥接模式通过组合替代继承的方式,避免随着维度增多导致继承的子类数量过多。

3、桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。

 

缺点

1、桥接模式的引入会增加系统的理解成本与设计难度。要求正确识别出系统中两个(多个)独立变化的维度,因此其使用范围具有一定的局限性。




更多内容请关注微信公众号
zbpblog微信公众号

如果您需要转载,可以点击下方按钮可以进行复制粘贴;本站博客文章为原创,请转载时注明以下信息

张柏沛IT技术博客 > 面向对象和设计模式(十六)桥接模式——PHP语言实现

热门推荐
推荐新闻