再谈 php设计模式之适配器(adapter)模式

适配器模式(Adapter)模式:将一个类的接口,转换成客户期望的另一个类的接口。适配器让原本接口不兼容的类可以合作无间。


【适配器模式中主要角色】

  • 目标(Target)角色:定义客户端使用的与特定领域相关的接口,这也就是我们所期待得到的

  • 源(Adaptee)角色:需要进行适配的接口

  • 适配器(Adapter)角色:对Adaptee的接口与Target接口进行适配;适配器是本模式的核心,适配器把源接口转换成目标接口,此角色为具体类。

其实也就是你家墙上有一个两口的插座(Adaptee),但你买了一个电风扇(Target)需要三个口的,这个时候你就需要一个插排(Adapter)。


【对象适配器模式PHP示例】

对象适配器使用的是委派, 对象适配器采用“对象组合”的方式,更符合松耦合精神。

/**
 * 目标角色
 */
interface Target 
{

    /**
     * 源类也有的方法1
     */
    public function sampleMethod1();

    /**
     * 源类没有的方法2
     */
    public function sampleMethod2();
}

/**
 * 源角色
 */
class Adaptee 
{

    /**
     * 源类含有的方法
     */
    public function sampleMethod1() {
        echo 'Adaptee sampleMethod1 <br />';
    }
}

/**
 * 类适配器角色
 */
class Adapter implements Target 
{

    private $_adaptee;

    public function __construct(Adaptee $adaptee) 
    {
        $this->_adaptee = $adaptee;
    }

    /**
     * 委派调用Adaptee的sampleMethod1方法
     */
    public function sampleMethod1() 
    {
        $this->_adaptee->sampleMethod1();
    }

    /**
     * 源类中没有sampleMethod2方法,在此补充
     */
    public function sampleMethod2() 
    {
        echo 'Adapter sampleMethod2 <br />';
    }

}

class Client 
{

    /**
     * Main program.
     */
    public static function main() 
    {
        $adaptee = new Adaptee();
        $adapter = new Adapter($adaptee);
        $adapter->sampleMethod1();
        $adapter->sampleMethod2();

    }

}


【类适配器模式PHP示例】(不推荐)

类适配器使用的是继承, 类适配器采用“多继承”的实现方式,带来了不良的高耦合,所以一般不推荐使用

/**
 * 目标角色
 */
interface Target 
{

    /**
     * 源类也有的方法1
     */
    public function sampleMethod1();

    /**
     * 源类没有的方法2
     */
    public function sampleMethod2();
}

/**
 * 源角色
 */
class Adaptee 
{

    /**
     * 源类含有的方法
     */
    public function sampleMethod1() 
    {
        echo 'Adaptee sampleMethod1 <br />';
    }
}

/**
 * 类适配器角色
 */
class Adapter extends Adaptee implements Target 
{

    /**
     * 源类中没有sampleMethod2方法,在此补充
     */
    public function sampleMethod2() 
    {
        echo 'Adapter sampleMethod2 <br />';
    }

}


class Client 
{

    /**
     * Main program.
     */
    public static function main() 
    {
        $adapter = new Adapter();
        $adapter->sampleMethod1();
        $adapter->sampleMethod2();

    }

}


例子2:以适配器模式发送短信

大佬们在对接信息、支付类的接口时,经常会使用这些平台提供的SDK。特别是有了composer之后,安装SDK就更加的方便了,但是,又有一个严重的问题,这帮人做的SDK虽说功能实现大同小异,但命名可是千差万别啊!!我们的系统原来一直使用的阿里云的业务,但是这回要增加极光和百度云的信息功能,一来做个后备,二来根据不同业务使用不同的接口达到安全或节约的目的,有没有办法统一一下他们对外的接口,让我们使用他们的SDK时能够非常方便的和之前使用大家都已经很习惯的阿里云的接口一样呢?当然有,给他们各自都上个适配器呗,实例化的时候大不了外面再套个工厂返回不同的适配器就好啦,只要适配器里的实现方法和阿里云一样就OK啦!

短信发送类图:

1111111.png

代码示例如下:

class Message
{

    public function send()
    {
        echo "阿里云发送短信!" . PHP_EOL;
    }

    public function push()
    {
        echo "阿里云发送推送!" . PHP_EOL;
    }
}



class JiguangSDKAdapter extends Message
{
    private $message;
    
    public function __construct($message)
    {
        $this->message = $message;
    }
    
    public function send()
    {
        $this->message->send_out_msg();
    }

    public function push()
    {
        $this->message->push_msg();
    }

}


class JiguangMessage
{

    public function send_out_msg()
    {
        echo "极光发送短信!" . PHP_EOL;
    }

    public function push_msg()
    {
        echo "极光发送推送!" . PHP_EOL;
    }

}

class BaiduYunSDKAdapter extends Message
{
    private $message;
    
    public function __construct($message)
    {
        $this->message = $message;
    }
    
    public function send()
    {
        $this->message->transmission_msg();
    }

    public function push()
    {
        $this->message->transmission_push();
    }

}

class BaiduYunMessage
{

    public function transmission_msg()
    {
        echo "百度云发送短信!" . PHP_EOL;
    }

    public function transmission_push()
    {
        echo "百度云发送推送!" . PHP_EOL;
    }

}

//实例化对象
$jiguangMessage  = new JiguangMessage();
$baiduYunMessage = new BaiduYunMessage();
$message         = new Message();

//原来的老系统发短信,使用阿里云
$message->send();
$message->push();

// 部分模块用极光发吧
$jgAdatper = new JiguangSDKAdapter($jiguangMessage);
$jgAdatper->send();
$jgAdatper->push();

// 部分模块用百度云发吧
$bdAatper = new BaiduYunSDKAdapter($baiduYunMessage);
$bdAatper->send();
$bdAatper->push();

解释说明:

  • 在这个例子中,我们有两个适配器,因为有两个SDK需要我们去适配,谁说只能有一个电源转换器,万一哪个神奇的国度是用500伏的电压呢,所以还是多带个电源转换器吧

  • 这里我们是继承的Message类,因为Message类是之前已经写好的代码,里面可能有一些可以公用的方法,所以并没有做接口抽象。可以考虑在重构代码的时候实现提取一个抽象接口,但在这里只是为了演示适配器不一定只是能去针对接口,只要和原对象保持一致,不去继承什么也是可以的,毕竟我们是弱类型语言,如果是类似于Java的强类型,那么继承或者实现还是很有必要的(多态性)

  • 组合式的适配器与装饰器类似,都会维护一个外部对象,装饰器更多的会使用原来的类中的方法,对其进行增加功能的操作,而适配器则很少去增加功能,而是直接替换掉

  • Laravel中的Filesystem模块,有一个FilesystemAdapter类,我觉得没啥可说的了,很明显的告诉大家咱用了适配器模式,好好研究一下吧


尾声总结:

当你想使用一个类,但他提供的内容跟你的业务又不太匹配的时候;或者你想创建一个类,可以与其他不相关的类或不可预见的类协同工作的时候,不妨试试适配器模式吧。



参考资料:

https://www.php.cn/php-weizijiaocheng-478902.html

https://mp.weixin.qq.com/s/T9fPRykDGVI3k1VKY0EmYw



推荐阅读:php设计模式之适配器(adapter)模式



声明:禁止任何非法用途使用,凡因违规使用而引起的任何法律纠纷,本站概不负责。

小周博客
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

精彩评论

全部回复 0人评论 7,777人参与

loading