浅谈依赖注入(DI)和控制反转(IoC)

依赖注入(DI)和控制反转(IoC)乍一看是很高端的东西,其实就是近年来兴起的一种思想,而又不仅仅是编程思想。主要是协调各组件间相互的依赖关系,同时大大提高了组件的可移植性,组件的重用机会也变得更多,接下来我们来了解一下这两个东西究竟是什么东西。

一、依赖注入(DI)

简单地讲,只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于依赖注入(DI)
举个栗子,依赖就是实现一件事情的前提,例如你想生孩子,嗯那好,你得首先有个老婆,现在没车没房没存款哪这么容易讨得到老婆,呸呸,扯远了。

class Baby
{
    protected $wife;
    public function __construct(Wife $wife)
    {
        $this->wife = $wife;
    }
}
$wife = new Wife();
$baby = new Baby($wife);

如代码所示,要生个孩子,你得有个老婆,老婆就是生孩子的依赖,并且需要在构造是将依赖注入才能实例化。
有人说那要生孩子只需要有个老婆而已,这样也不难啊,我只能说兄弟你太年轻了,先在如果生孩子你岳母还要你有房子、车子、准生证....一大堆前提,甚至科学技术发达以后生孩子连老婆都不需要了呢,这样我们就不得不重写Baby类了,这样显然耦合性太高了。这时候我们可以想到用工厂模式来组模,即将生孩子的条件在一个工厂类中完成,这样Baby类就从原来对各种类的依赖变成了对工厂类的依赖,如果生孩子的依赖变了我们不需要修改Baby类而只需要修改这个工厂类。
Baby类如下:

class Baby
{
    protected $condition;

    public function __construct(array $modules)
    {
        // 初始化工厂
        $factory = new BirthbabyModuleFactory();

        // 通过工厂提供的方法制造需要的模块
        foreach ($modules as $key=>$value) {
            $this->condition[] = $factory->makeModule($key, $value);
        }
    }
}

工厂类如下:

class BirthbabyModuleFactory
{
    public function makeModule($moduleName, $options)
    {
        switch ($moduleName) {
            case 'Wife':
                return new Wife($options[0], $options[1]);
            case 'Car':
                return new Car($options[0]);
            case 'Build':
                return new Build($options[0], $options[1], $options[2]);
        }
    }
}

然而,工厂类还是有一定的不足,那就是,接口未知,即没有一个很好的契约,这时我们需要提出一种契约,这样无论是谁创造出的模组,都符合这样的接口,自然就可被正常使用,于是我们想到了接口( interface )。

因为一个 对象(object) 本身是由他的模板或者原型 —— 类 (class) ,经过实例化后产生的一个具体事物,而有时候,实现统一种方法且不同功能(或特性)的时候,会存在很多的类(class),这时候就需要有一个契约,让大家编写出可以被随时替换却不会产生影响的接口。
interface BirthbabyModuleInterface
{
    public function activate(array $target);
}
//所有implements这个interface的类必须遵循interface的方法
class Wife implements BirthbabyModuleInterface
{
    public function activate(array $target)
    {
        //
    }
}

class Car implements BirthbabyModuleInterface
{
    public function activate(array $target)
    {
        //
    }
}

然而不管怎样,在初始化Baby对象的时候总是需要手动注入依赖,在人类社会高度发达的今天居然还要手动生孩子?不是说好的坐上来自己动的吗。于是更高级的工厂应运而生——IoC 容器。

二、控制反转(IoC)

控制反转,顾名思义,就是把控制权反转过来,举个栗子,以前生孩子需要有老婆、房子、准生证。。。,控制权在老婆、房地产商、街道办事处的手上,你要生孩子得征得他们的同意,现在呢你有一个好妈妈,她也希望早点抱孙子,于是他把你生孩子的一切条件都准备好了,老婆哪来的?越南买的。这时候你就会发现生孩子这件事你变成了话事人,你想啥时候生啥时候生,想生几个生几个,这个好妈妈把老婆、车子、准生证都绑定在一个容器中让你随时能拿,这个容器就是IoC 容器。简单地说,一个类需要绑定、注册至容器中,才能被“制造,然后还需要有一个专门提供这个容器中的服务的服务提供者,下面是一个简单地容器类:

class Container
{
    protected $binds;

    protected $instances;
  //绑定
    public function bind($abstract, $concrete)
    {
        if ($concrete instanceof Closure) {
            $this->binds[$abstract] = $concrete;
        } else {
            $this->instances[$abstract] = $concrete;
        }
    }
//提供
    public function make($abstract, $parameters = [])
    {
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }
        array_unshift($parameters, $this);

        return call_user_func_array($this->binds[$abstract], $parameters);
    }
}

通过最初的 绑定(bind) 操作,我们向 超级工厂 注册了一些生产脚本,这些生产脚本在生产指令下达之时便会执行。发现没有?我们彻底的解除了 孩子 与 生孩子组模 的依赖关系,更重要的是,容器类也丝毫没有和他们产生任何依赖!我们通过注册、绑定的方式向容器中添加一段可以被执行的回调(可以是匿名函数、非匿名函数、类的方法)作为生产一个类的实例的脚本 ,只有在真正的 生产(make) 操作被调用执行时,才会触发。

$container = new Container;

// 向该 超级工厂添加孩子的生产脚本
$container->bind('baby', function($container, $moduleName) {
    return new Baby($container->make($moduleName));
});

// 向该 超级工厂添加超能力模组的生产脚本
$container->bind('xpower', function($container) {
    return new wife;
});
$container->bind('ultrabomb', function($container) {
    return new house;
});

// 开始启动生产
$superman_1 = $container->make('baby', 'wfie');
$superman_2 = $container->make('baby', 'house');
$superman_3 = $container->make('baby', 'car');

总的来说,IoC容器就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展。IOC理论提出的观点大体是这样的:借助于“第三方”实现具有依赖关系的对象之间的解耦
最后再引出一个经典的栗子,那就是关于数据库连接的。假如你有一个类需要用到数据库,这时候你可以这样写:

class A
 {
 
     public function __construct()
     {
         $connection = new Connection(array(
             "host" => "localhost",
             "username" => "root",
             "password" => "secret",
             "dbname" => "invo"
         ));
     }
 }

 $some = new A();

有的老铁说没毛病,然而显而易见的是你没次初始化这个类的对象你都要重新连接一次数据库,更重要的是但你想把数据库从oracle换成Mysql的时候你又得去修改这个类,这样耦合度就高了,解决办法就是把创建一个数据库连接类并且注册到服务容器中去,然后初始化,这样这个数据库实例就存在容器中了,实际上这个数据库连接类是一个单例,不会一直初始化,这样其实就是一个解耦的过程。
上图,途中箭头为依赖关系


image.png
image.png

本来各个类之间错综复杂的相互依赖变成了各个类对IoC容器的依赖,类A、B、C、D、间不存在耦合,类与类之间失去了直接联系,所以,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。
以上都是本人自己的见解,如有不对,那你又能怎样。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容