🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## **桥接(Bridge)模式:** 将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现的,从而降低了抽象和实现这两个可变维度的耦合度。 特点:独立存在,扩展性强 应用:需要不断更换调用对象却执行相同的调用方法,实现扩展功能 将不同的对象传入桥接对象中通过set和get获取相应的对象的内容 **桥接(Bridge)模式的优点:** * 由于抽象与实现分离,所以扩展能力强; * 其实现细节对客户透明。 **缺点是:** * 由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,这增加了系统的理解与设计难度。 ``` abstract class Fruit{ abstract function getColor(); } class Apple extends Fruit{ public function getColor(){ return'red'; } } class Banana extends Fruit{ public function getColor(){ return'yellow'; } } class BridgeObj{ protected $_fruit; /** * 设置对象 */ public function setFruit($fruit){      $this->_fruit = $fruit; } public function getColor(){ return $this->_fruit->getColor(); } } $obj =newBridgeObj(); $obj->setFruit(new Apple()); printf("本次桥接对象:%sn", $obj->getColor()); $obj->setFruit(newBanana()); printf("本次桥接对象:%sn", $obj->getColor()); ``` **(一)为什么需要桥接模式** 1,如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。 2,抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。 3,虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。 **(二)桥接模式UML图** ![图片描述](https://img.mukewang.com/58dbc01c0001e8e910030543.png "PHP设计模式(九)—桥接模式(BridgePattern)_") * Abstraction为抽象化角色,主要职责是定义该角色的行为,同时保存一个对实现化角色的引用。 * Implementor为实现化角色,它是一个接口或抽象类。在PHP中一般是抽象类。 有点不知所云吧。我学的时候也是。老规矩,来个实例吧。 **(三)简单实例** 如果我现在是小米公司的雷布斯,小米公司旗下有小米mix和小米note手机,现在有一个底层的语音输出软件,由于小米mix的全面屏设计没有开孔使用了骨传导,所以和小米note有些不同。那么我们要如何来设计这个输出功能呢? 传统的做法就应该是,一个抽象手机品牌,mix和note手机品牌继承抽象手机品牌。然后有一个mix品牌的输出软件继承自mix品牌。如果这时候有一个redmi的牌子的输出软件就应继承自redmi品牌,redmi继承抽象手机品牌。 如果除了这个语音输出软件,我们还有其它的软件呢,一个手机有很多软件,那么这种继承,如果画出继承链的话,那一个品牌下得有多少子类? 如果我们使用桥接模式的话,就可以把这个软件的具体实现与抽象品牌分离出来,这样也能是我们动态增减实现化角色时更为方便。 ~~~ <?php //抽象化角色 abstract class MiPhone{ protected $_audio; //存放音频软件对象 abstract function output(); public function __construct(Audio $audio){ $this->_audio = $audio; } } //具体手机 class Mix extends MiPhone{ //语音输出功能 public function output(){ $this->_audio->output(); } } class Note extends MiPhone{ public function output(){ $this->_audio->output(); } } //实现化角色 功能实现者 abstract class Audio{ abstract function output(); } //具体音频实现者 -骨传导音频输出 class Osteophony extends Audio{ public function output(){ echo "骨传导输出的声音-----哈哈".PHP_EOL; } } //普通音频输出---声筒输出 class Cylinder extends Audio{ public function output(){ echo "声筒输出的声音-----呵呵".PHP_EOL; } } //让小米mix和小米note输出声音 $mix = new Mix(new Osteophony); $mix->output(); $note = new Note(new Cylinder); $note->output(); ~~~ 这样写的好处是把抽象化角色手机和实现化角色手机的具体功能音频输出 分离了出来。如果现在最新的小米note系列也要用上骨传导输出,那么我们只需实例化时传入的声筒音频类改为骨传导音频类。如果我们还有一个扬声器输出,小米mix和小米note都有这个功能。我们只需要再添加一个扬声器输出类继承Audio类,然后谁使用就保存这个实例在属性中。没有桥接模式的话,我们可是要写两个,一个是小米mix的扬声器输出,一个是小米note。 如果扬声器输出对小米mix和小米note不一样,那么我们确实需要写两个扬声器输出类。使用桥接模式,依然比原来直接继承好,就是因为有一天扬声器技术驱动更新了,我们要更新扬声器不用修改手机类代码,而只需传入一个更新的扬声器类。 在比如: ![](https://img.kancloud.cn/6f/59/6f59df50e8970f38ffea6065fbca7328_1041x233.png) 类图 ![](https://img.kancloud.cn/9f/7f/9f7f0c6d5354bfdde03f5531defddefa_1313x491.png) 示例代码: ``` //抽象类:电脑 abstract class Computer{ protected $phone; public function __construct($phone) { $this->phone = $phone; } public function &__get($property_name) { if(isset($this->$property_name)) { return($this->$property_name); } else { return(NULL); } } public function __set($property_name, $value) { $this->$property_name = $value; } public abstract function connect(); } //接口:手机 interface Phone{ public function connectImpl(); } //华硕品牌的电脑 class ASUSComputer extends Computer{ public function __construct($phone) { $this->phone=$phone; } public function connect() { echo "华硕电脑"; $this->phone->connectImpl(); } } //戴尔品牌的电脑 class DellComputer extends Computer{ public function __construct($phone) { $this->phone=$phone; } public function connect() { echo "戴尔电脑"; $this->phone->connectImpl(); } } //三星手机 class SamsungPhone implements Phone{ public function connectImpl() { echo "连接了三星手机\n"; } } //小米手机 class XiaomiPhone implements Phone{ public function connectImpl() { echo "连接了小米手机\n"; } } //抽象类:人 abstract class Person{ public $computer; public function __construct($computer) { $this->computer = $computer; } public abstract function useComputer(); } //学生 class Student extends Person{ public function useComputer() { echo "学生使用"; $this->computer->connect(); } } //老师 class Teacher extends Person{ public function useComputer() { echo "教师使用"; $this->computer->connect(); } } function main(){ //华硕电脑连接了小米手机 $asusComputer=new ASUSComputer(new XiaomiPhone()); $asusComputer->connect(); //戴尔电脑连接了三星手机 $dellComputer=new DellComputer(new SamsungPhone()); $dellComputer->connect(); //学生使用华硕电脑连接了小米手机 $student=new Student(new ASUSComputer(new XiaomiPhone())); $student->useComputer(); //教师使用戴尔电脑连接了三星手机 $teacher=new Teacher(new DellComputer(new SamsungPhone())); $teacher->useComputer(); } main(); 输出结果: 华硕电脑连接了小米手机 戴尔电脑连接了三星手机 学生使用华硕电脑连接了小米手机 ``` 举例:   车分为很多种(小轿车,公交车),并且每种车都会跑在不同的道路上(街道,高速路),如果使用继承的方式我们可以实现这些场景   但是那样做的话会使得代码变得可扩展行很差,但是使用桥接模式就不一样啦 ~~~ abstract class Road{ public $car; public function __construct(Car $car){ $this->car = $car; } public abstract function run(); } class SpeedWay extends Road{ public function run(){ echo $this->car->name." run on SpeedWay\n"; } } class Street extends Road{ public function run(){ echo $this->car->name." run on Street\n"; } } abstract class Car{ public $name; } class SmallCar extends Car{ public function __construct(){ $this->name = "SmallCar"; } } class Bus extends Car{ public function __construct(){ $this->name = "Bus"; } } $small_car = new SmallCar(); $SpeedWay = new SpeedWay($small_car); $SpeedWay->run(); $bus = new Bus(); $Street = new Street($bus); $Street->run(); ~~~ 例子: ![](https://img.kancloud.cn/3b/7e/3b7e0d7a04c514247c6ca5b0b82fb8ae_661x525.png) ``` <?php //实现化角色:颜色 interface Color{ function getColor(); } //具体实现化角色:黄色 class Yellow implements Color{ public function getColor(){ return "yellow"; } } //具体实现化角色:红色 class Red implements Color{ public function getColor(){ return "red"; } } //抽象化角色:包 abstract class Bag{ protected $color; public function setColor($color){ $this->color=$color; } abstract public function getName(); } //扩展抽象化角色:挎包 class HandBag extends Bag{ public function getName(){ return $this->color->getColor()."的HandBag(挎包)"; } } //扩展抽象化角色:钱包 class Wallet extends Bag{ public function getName(){ return $this->color->getColor()."的Wallet(钱包)"; } } class BagManage{ public static function main(){ $color=new Yellow(); $bag=new HandBag(); $bag->setColor($color); echo $bag->getName(); $color=new Red(); $bag=new Wallet(); $bag->setColor($color); echo $bag->getName(); } } BagManage::main(); ``` 使用场景:   当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。   当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。   当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。 ## 桥接模式模式的扩展 在软件开发中,有时桥接(Bridge)模式可与[适配器模式](http://c.biancheng.net/view/1361.html)联合使用。当桥接(Bridge)模式的实现化角色的接口与现有类的接口不一致时,可以在二者中间定义一个适配器将二者连接起来,其具体结构图如图 5 所示。 ![](https://img.kancloud.cn/a6/67/a66767a81c6c38956fe57fea4c49d44b_598x494.png)