首页 > 模式算法 > php中的桥接模式
2012
04-21

php中的桥接模式

概念

桥接模式 (Bridge Pattern):将抽象与实现解耦,使得两者可以独立的变化


1,如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。

2,抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。

3,虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。


场景

1、现需要提供大中小3种型号的画笔,能够绘制5种不同颜色,如果使用蜡笔,我们需要准备3*5=15支蜡笔,也就是说必须准备15个具体的蜡笔类。而如果使用毛笔的话,只需要3种型号的毛笔,外加5个颜料盒,用3+5=8个类就可以实现15支蜡笔的功能。

2、小米不同型号手机使用不同的底层语音输出软件

3、不同电脑可以连接不同的手机

4、发送多种类型的消息(qq,email),每种消息都有不同的消息等级(普通消息,紧急消息等)

示例

小米公司旗下有mix和小米note手机,现在有一个底层的语音输出软件,由于小米mix的全面屏设计没有开孔使用了骨传导,所以和小米note有些不同。那么我们要如何来设计这个输出功能呢?

传统的做法就应该是,一个抽象手机品牌,mix和note手机品牌继承抽象手机品牌。然后有一个品牌的输出软件继承自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();

这样写的好处是把抽象化角色手机和实现化角色手机的具体功能音频输出 分离了出来。

使用桥接模式,依然比原来直接继承好,就是因为有一天扬声器技术驱动更新了,我们要更新扬声器不用修改手机类代码,而只需传入一个更新的扬声器类。


示例2

<?php

//抽象类:电脑
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();
    }

}


//华硕电脑连接了小米手机
$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();

解决了什么问题

当一个抽象可能有多个实现的时,通常用继承来协调它们。抽象类定义对该抽象的接口而具体的子类则用不同的方式加以实现。但是此方法有时不够灵活。继承机制将抽象部分与它的实现部分固定在一起,使得难以对抽象部分和实现部分独立地修改,扩充和重用。


桥接模式和装饰模式  这两个模式在一定程度上都可减少子类的数目,避免出现复杂的继承关系,但是它们解决的方法却各有不同


装饰模式把子类中比基类多出来的部分放到单独的类里面,以适应新功能的添加,把描述新功能的类封装到基类的对象里面时,就得到了所需要的子类对象,这些描述新功能的类通过组合可以实现很多的功能组合。


桥接模式是把两个以上独立的抽象维度分离,使用聚合的方式使其关联,来达到减少子类的目的,结构上桥接模式比装饰模式要复杂。


桥接模式和适配器模式它们的共同点是桥接和适配器都是让两个类配合工作,它们的区别是出发点不同,适配器的出发点是改变已有的两个接口,让它们相容,可以结合那些功能上相似但是接口不同的类,桥接模式的出发点是分离抽象化和实现化,是两者的接口可以不同,目的是分离。



本文》有 0 条评论

留下一个回复