关于依赖注入(DI) 和 控制反转(IoC)的一点总结

一、名词解释

IoC - Inversion of Control 控制反转
DI - Dependency Injection 依赖注入
依赖倒置原则(DIP):一种软件架构设计的原则(抽象概念),依赖倒置原则,它转换了依赖,抽象模块不依赖于具体模块的实现,而具体模块依赖于抽象模块定义的接口。通俗的讲,就是抽象模块定义接口,具体模块负责实现。。
控制反转(IoC):一种反转流、依赖和接口的方式(DIP的具体实现方式)。
依赖注入(DI):IoC的一种实现方式,用来反转依赖(IoC的具体实现方式)。
IoC容器 :依赖注入的框架,用来映射依赖,管理对象创建和生存周期(DI框架)。

依赖注入(DI)是一个过程,通过这个过程,对象可以通过构造函数参数,工厂方法的参数或者在构造或返回对象实例后设置的属性来定义它们的依赖关系从工厂方法。

依赖注入和控制反转说的实际上是同一个东西,它们是一种设计模式,这种设计模式用来减少程序间的耦合。依赖注入和控制反转是对同一件事情的不同描述,从某个方面讲,就是它们描述的角度不同。

1、依赖注入是从应用程序的角度在描述,可以把依赖注入,即:应用程序依赖容器创建并注入它所需要的外部资源;

2、而控制反转是从容器的角度在描述,即:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。

整个过程中参与者都有谁?

一般有三方参与者,一个是某个对象;一个是IoC/DI的容器;另一个是某个对象的外部资源。
1、某个对象指的就是任意的、普通的PHP对象; 
2、IoC/DI的容器简单点说就是指用来实现IoC/DI功能的一个框架程序;
3、对象的外部资源指的就是对象需要的,但是是从对象外部获取的,都统称资源,比如:对象需要的其它对象、或者是对象需要的文件资源等等。

二、控制反转IoC进一步解释

1、如何反转,反转了什么

控制反转顾名思义,就是要去反转控制权,那么到底是哪些控制被反转了?控制反转是指把对象的依赖管理从内部转移至外部。

在单一职责原则的设计下,很少有单独一个对象就能完成的任务。大多数任务都需要复数的对象来协作完成,这样对象与对象之间就有了依赖。一开始对象之间的依赖关系是自己解决的,需要什么对象了就New一个出来用,控制权是在对象本身。但是这样耦合度就非常高,可能某个对象的一点小修改就会引起连锁反应,需要把依赖的对象一路修改过去。

如果依赖对象的获得被反转,具体生成什么依赖对象和什么时候生成都由对象之外的IOC容器来决定。对象只要在用到依赖对象的时候能获取到就可以了,常用的方式有依赖注入和依赖查找(Dependency Lookup)。这样对象与对象之间的耦合就被移除到了对象之外,后续即使有依赖修改也不需要去修改原代码了。

2、依赖注入

控制反转是把对象之间的依赖关系提到外部去管理,可依赖是提到对象外面了,对象本身还是要用到依赖对象的,这时候就要用到依赖注入了。顾名思义,应用需要把对象所需要的依赖从外部注入进来。IoC有2种常见的实现方式:依赖注入和服务定位。

控制反转(IoC)一种重要的方式,就是将依赖对象的创建和绑定转移到被依赖对象类的外部来实现。

依赖注入(DI),它提供一种机制,将需要依赖(具体模块)对象的引用传递给被依赖(抽象模块)对象。
实现方式:

方法一 构造函数注入
构造函数函数注入传递依赖。根据DIP原则,我们知道抽象模块不应该依赖于具体模块,两者应该依赖于抽象。那么构造函数的参数应该是一个抽象类型。

方法二 属性注入
属性注入是通过属性来传递依赖。

方法三 接口注入
相比构造函数注入和属性注入,接口注入显得有些复杂,使用也不常见。具体思路是先定义一个接口,包含一个设置依赖的方法。然后依赖类,继承并实现这个接口。

对于大型项目来说,相互依赖的组件比较多。如果还用手动的方式,自己来创建和注入依赖的话,显然效率很低,而且往往还会出现不可控的场面。正因如此,IoC容器诞生了。IoC容器实际上是一个DI框架,它能简化我们的工作量。

它包含以下几个功能:
1、动态创建、注入依赖对象。
2、管理对象生命周期。
3、映射依赖关系。

三、PHP代码示例

PHP具体代码实现的思路:


1、Ioc容器维护binding数组记录bind方法传入的键值对如:log=>FileLog, user=>User
2、在ioc->make('user')的时候,通过反射拿到User的构造函数,拿到构造函数的参数,发现参数是User的构造函数参数log,然后根据log得到FileLog。
3、这时候我们只需要通过反射机制创建 $filelog = new FileLog();
4、通过newInstanceArgs然后再去创建new User($filelog);


interface log{
    public function write();
}

// 文件记录日志
class FileLog implements Log{
    public function write(){
        echo 'file log write...';
    }
}

// 数据库记录日志
class DatabaseLog implements Log{
    public function write(){
        echo 'database log write...';
    }
}

class User{
    protected $log;
    public function __construct(Log $log){
        $this->log = $log;
    }
    public function login(){
        // 登录成功,记录登录日志
        echo 'login success...';
        $this->log->write();
    }
}

class Ioc{
    public $binding = [];

    public function bind($abstract, $concrete){
        //这里为什么要返回一个closure呢?因为bind的时候还不需要创建User对象,所以采用closure等make的时候再创建FileLog;
        $this->binding[$abstract]['concrete'] = function ($ioc) use ($concrete) {
            return $ioc->build($concrete);
        };
    }

    public function make($abstract){
        // 根据key获取binding的值
        $concrete = $this->binding[$abstract]['concrete'];

        return $concrete($this);
    }

    // 创建对象
    public function build($concrete) {
        $reflector = new ReflectionClass($concrete);
        $constructor = $reflector->getConstructor();
        if(is_null($constructor)) {
            return $reflector->newInstance();
        }
            $dependencies = $constructor->getParameters();
            $instances = $this->getDependencies($dependencies);

            return $reflector->newInstanceArgs($instances);
    }

    // 获取参数的依赖
    protected function getDependencies($paramters) {
        $dependencies = [];
        foreach ($paramters as $paramter) {
            $dependencies[] = $this->make($paramter->getClass()->name);
        }

        return $dependencies;
    }
}

//实例化IoC容器
$ioc = new Ioc();
$ioc->bind('log','FileLog');
$ioc->bind('user','User');
$user = $ioc->make('user');
$user->login();

参考

1、聊一聊PHP的依赖注入(DI) 和 控制反转(IoC)
2、原则&模式|理解DIP、IoC、DI以及IoC容器
3、Laravel 服务容器实例教程 —— 深入理解控制反转(IoC)和依赖注入(DI)(推荐阅读)
4、如何实现 IoC 容器和服务提供者是什么概念(推荐阅读)
5、 [Laravel 5.8 文档 ] 底层原理 —— 服务容器(推荐阅读)
6、PHP反射机制实现自动依赖注入
7、那些年搞不懂的高深术语——依赖倒置•控制反转•依赖注入•面向接口编程
8、控制反转(IOC)和依赖注入(DI)(推荐阅读)
9、控制反转和依赖注入的理解
10、谈谈php里的IOC控制反转,DI依赖注入

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

推荐阅读更多精彩内容