保姆级讲解依赖注入(DI)和控制反转(IOC)

控制反转是一种思想,而依赖注入是实现控制反转的一种方式

很多想表述的内容基本都写在注释里了,我觉得这样阅读比较方便,针对代码来讲才是核心,而不是整一堆名词,抛一些模棱两可的概念,弄一些华而不实的东西。以下代码我自己review过2遍,我自认为稍微有一点基础的都可以看懂。

<?php
/**
 * 就用发邮件的例子来举例子
 * 首先写一个邮件发送者的接口
 * 接口里面有个发送方法 send()
 * Interface EmailSenderInterface
 */
interface EmailSenderInterface
{
    public function send();
}

/**
 * 谷歌发送者类实现了邮件发送者的接口
 * Class: GmailSender
 * Date: 2021/5/18
 */
class GmailSender implements EmailSenderInterface{

    private static $instance = null;

    /**
     * 单例实现
     * Date: 2021/5/18
     * @return GmailSender|null
     */
    public static function getInstance()
    {
        if (self::$instance == null) {
            return self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Override
     * 接口中的方法必须被实现(重写)
     * Date: 2021/5/18
     * @return string
     */
    public function send()
    {
        return '这是来自谷歌发送的验证码:123456';
    }
}

/**
 * 假设我这里是一个发送邮件验证码的类
 * Class: SendEmail
 * Date: 2021/5/18
 */
class SendEmail
{
    private $_emailSender;

    /**
     * 这里是依赖注入这个概念的核心,
     * 当new SendEmail();这个类的时候会调用__construct()方法,
     * 所以当我使用这个类的时候,我就在这个构造方法里去写我需要的其他类,
     * 或者其他配置不就好了?是的,然后定义一个属性,然后进行赋值。这样,
     * SendEmail类中的 $_emailSender 属性便变成了一个谷歌邮箱发送者的实例。
     * SendEmail constructor.
     */
    public function __construct()
    {
        //这是谷歌邮箱
        $this->_emailSender = GmailSender::getInstance();
    }

    /**
     * 发送邮箱验证码
     * Date: 2021/5/18
     * @return string
     */
    public function sendCode()
    {
        /**
         * $this->_emailSender 相当于 GmailSender::getInstance();
         * return $this->_emailSender->send();等价于return GmailSender::getInstance()->send();
         */
        return $this->_emailSender->send();
    }
}
$sendEmail = new SendEmail();
print_r($sendEmail->sendCode());

在这里插入图片描述
邮件发送成功了。
测试通过,上线了。
好产品来了,谷歌邮箱不太好,用户反应邮件不是延时就是收不到,经过考虑决定新增个QQ邮箱。

新增

/**
 * 同样的集成邮件发送接口
 * Class: QQSender
 * Date: 2021/5/18
 */
class QQSender implements EmailSenderInterface{

    private static $instance = null;


    public static function getInstance()
    {
        if (self::$instance == null) {
            return self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Override
     * 接口中的方法必须被实现(重写)
     * Date: 2021/5/18
     * @return string
     */
    public function send()
    {
        return '这是来自QQ发送的验证码:654321';
    }
}

然后去扩充SendEmail类中的构造方法新增一个QQ邮箱的依赖:
例如这样(错误示范)

class SendEmail
{
    private $_emailSender;
    //新增一个qq邮箱私有属性
    private $_emailQQSender;

    public function __construct()
    {
        //这是谷歌邮箱
        $this->_emailSender = GmailSender::getInstance();
        //这是新增的qq邮箱
        $this->_emailQQSender = QQSender::getInstance();
    }

    /**
     * 发送谷歌邮箱验证码
     * Date: 2021/5/18
     * @return string
     */
    public function sendGmailCode()
    {
        return $this->_emailSender->send();
    }

    /**
     * 发送qq邮箱验证码
     * Date: 2021/5/18
     * @return string
     */
    public function sendQQCode()
    {
        return $this->_emailQQSender->send();
    }
}
$sendEmail = new SendEmail();
print_r($sendEmail->sendQQCode());
echo PHP_EOL;
print_r($sendEmail->sendGmailCode());

die;

在这里插入图片描述
好功能实现了。
测试->上线,产品又双叒叕来了,这次说得批量添加邮箱,要支持所有市面上的邮箱。这可怎么办,然后核心概念主角就出来了

依赖注入,抛出概念之前,可以先想一下,我们要解决什么问题?
要解决的是如何降低耦合,如何变成的可以在外边就可以控制用哪个邮箱发送邮件,而不是用每次去修改构造方法,重新创建个发送方法。

首先看一下现有的类的结构,有两个邮箱属性和2个发送方法。

class SendEmail
{
    private $_emailSender;
    //新增一个qq邮箱私有属性
    private $_emailQQSender;

    public function __construct()
    {
        //这是谷歌邮箱
        $this->_emailSender = GmailSender::getInstance();
        //这是新增的qq邮箱
        $this->_emailQQSender = QQSender::getInstance();
    }

    /**
     * 发送谷歌邮箱验证码
     * Date: 2021/5/18
     * @return string
     */
    public function sendGmailCode()
    {
        return $this->_emailSender->send();
    }

    /**
     * 发送qq邮箱验证码
     * Date: 2021/5/18
     * @return string
     */
    public function sendQQCode()
    {
        return $this->_emailQQSender->send();
    }
}

调用时候是这样的

$sendEmail = new SendEmail();
//发送qq邮箱验证码
print_r($sendEmail->sendQQCode());
echo PHP_EOL;
//发送谷歌邮箱验证码
print_r($sendEmail->sendGmailCode());
die;

不难发现,每次去新增邮箱都需要重新扩展方法,都需要重新修改构造方法,那能不能不修改呢?不扩展呢?

观察一下构造函数

public function __construct()
    {
        //这是谷歌邮箱
        $this->_emailSender = GmailSender::getInstance();
        //这是新增的qq邮箱
        $this->_emailQQSender = QQSender::getInstance();
    }

有没有发现构造函数做的事情其实是一件事情,就是把实例化后的对象赋值给当前类的属性。
然后优化一下

class SendEmail
{
    private $_emailSender;
    /**
     * 在构造函数值添加形参,因为要实现在外面传入所以构造方法是类的唯一入参方式。
     * SendEmail constructor.
     * @param $emailSender 这是外面传入进来的实例
     */
    public function __construct($emailSender)
    {
        //将传入进来的实例赋值给本类的私有属性上
        $this->_emailSender = $emailSender;
    }

    /**
     * 调用传入进来的实例的send方法
     * Date: 2021/5/18
     * @return string
     */
    public function sendCode()
    {   
        //return $this->_emailSender->send();
        //实际等价于:
        //第一步:$this->_emailSender = 构造函数传进来的实例
        //第二步:调用实例中的send()方法;
        return $this->_emailSender->send();
    }
}

然后调用一下

$qq = new SendEmail(QQSender::getInstance());
print_r($qq->sendCode());
echo PHP_EOL;
$gmail = new SendEmail(GmailSender::getInstance());
print_r($gmail->sendCode());
die;

在这里插入图片描述

不难发现,现在的类已经不需要修改了,只需要传一个参数就可以了,在邮件发送类的外部,注入邮箱的类型,就完成了多邮箱的适配发送。邮箱发送类的控制权,也交给了调用者(控制反转)。

总结:依赖注入,最终的实现思想就是就是降低耦合,把内部依赖变成以注入的方式去实现。换句话说不在类内部使用new来创建依赖对象,而是将其在外部创建好后,通过构造函数或者赋值函数注入进类中,也就是当参数传进去。这样做的同时,控制权也发生了转变,即本来控制权在邮箱发送类的手里,现在缺交给了调用者调用时传入的参数来决定,这个过程其实就叫控制反转(IOC)与依赖注入(DI)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PHP开光程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值