PHP依赖注入(DI)和控制反转(IoC)详解
起首依靠注入和节制反转说的是统一个对象,是一种计划模式,这种计划模式用来镌汰措施间的耦合,不才进修了一下,看TP官网还没有相干的文章,就写下这篇拙作先容一下这种计划模式,但愿能为TP社区孝顺一些力气。 起首先别追究这个计划模式的界说,不然你必然会被说的云里雾里,笔者就是深受其害,百度了N多文章,都是从理论角度来描写,充斥着大量的生涩词汇,要么就是java代码描写的,也生涩。 不管怎么样,总算弄清晰一些了,下面就以php的角度来描写一下依靠注入这个观念。 先假设我们这里有一个类,类内里必要用到数据库毗连,凭证最最原始的步伐,我们也许是这样写这个类的: private $_db;function __construct(){ include "./Lib/Db.php"; $this->_db = new Db("localhost","root","123456","test"); } function getList(){ $this->_db->query("......");//这里详细sql语句就省略不写了 } } 进程:在结构函数里先将数据库类文件include进来; 然后又通过new Db并传入数据库毗连信息实例化db类; 之后getList要领就可以通过$this->_db来挪用数据库类,实现数据库操纵。 看上去我们实现了想要的成果,可是这是一个恶梦的开始,往后example1,example2,example3....越来越多的类必要用到db组件,假如都这么写的话,万一有一天数据库暗码改了可能db类产生变革了,岂不是要转头修改全部类文件? ok,为了办理这个题目,工场模式呈现了,我们建设了一个Factory要领,并通过Factory::getDb()要领来得到db组件的实例: sample类酿成: private $_db;function __construct(){ $this->_db = Factory::getDb(); } function getList(){ $this->_db->query("......");//这里详细sql语句就省略不写了 } } 这样就美满了吗?再次想想一下往后example1,example3....全部的类,你都必要在结构函数里通过Factory::getDb();获的一个Db实例,现实上你由原本的直接与Db类的耦合变为了和Factory工场类的耦合,工场类只是帮你把数据库毗连信息给包装起来了,固然当数据库信息产生变革时只要修改Factory::getDb()要领就可以了,可是溘然有一天工场要领必要更名,可能getDb要领必要更名,你又怎么办?虽然这种需求其拭魅照旧很操蛋的,但偶然辰确实存在这种环境,一种办理方法是: 我们不从example类内部实例化Db组件,我们依赖从外部的注入,什么意思呢?看下面的例子: _db->query("......");//这里详细sql语句就省略不写了 } //从外部注入db毗连 function setDb($connection){ $this->_db = $connection; } } //挪用 $example = new example(); $example->setDb(Factory::getDb());//注入db毗连 $example->getList();这样一来,example类完全与外部类扫除耦合了,你可以看到Db类内里已经没有工场要领或Db类的身影了。我们通过从外部挪用example类的setDb要领,将毗连实例直接注入进去。这样example完全不消体谅db毗连怎么天生的了。 这就叫依靠注入,实现不是在代码内部建设依靠相关,而是让其作为一个参数转达,这使得我们的措施更轻易维护,低落措施代码的耦合度,实现一种松耦合。 这还没完,我们再假设example类内里除了db还要用到其他外部类,我们通过: setDb(Factory::getDb());//注入db毗连 $example->setFile(Factory::getFile());//注入文件处理赏罚类 $example->setImage(Factory::getImage());//注入Image处理赏罚类 ...我们没完没了的写这么多set?累不累? ok,为了不消每次写这么多行代码,我们又去弄了一个工场要领: setDb(Factory::getDb());//注入db毗连 $example->setFile(Factory::getFile());//注入文件处理赏罚类 $example->setImage(Factory::getImage());//注入Image处理赏罚类 return $expample; } }实例化example时变为: getList();好像美满了,可是怎么感受又回到了上面第一次用工场要领时的场景?这确实不是一个好的办理方案,以是又提出了一个观念:容器,又叫做IoC容器、DI容器。 我们原来是通过setXXX要领注入各类类,代码很长,要领许多,固然可以通过一个工场要领包装,可是还不是那么爽,好吧,我们不消setXXX要领了,这样也就不消工场要领二次包装了,那么我们还怎么实现依靠注入呢? 这里我们引入一个约定:在example类的结构函数里传入一个名为Di $di的参数,如下: _di = $di; } //通过di容器获取db实例 function getList(){ $this->_di->get('db')->query("......");//这里详细sql语句就省略不写了 } } $di = new Di(); $di->set("db",function(){ return new Db("localhost","test"); }); $example = new example($di); $example->getList();Di就是IoC容器,所谓容器就是存放我们也许会用到的各类类的实例,我们通过$di->set()配置一个名为db的实例,由于是通过回调函数的方法传入的,以是set的时辰并不会当即实例化db类,而是当$di->get('db')的时辰才会实例化,同样,在计划di类的时辰还可以融入单例模式。 这样我们只要在全局范畴内阐明一个Di类,将全部必要注入的类放到容器里,然后将容器作为结构函数的参数传入到example,即可在example类内里从容器中获取实例。虽然也不必然是结构函数,你也可以用一个 setDi(Di $di)的要领来传入Di容器,总之约定是你拟定的,你本身清晰就行。 这样一来依靠注入以及要害的容器观念已经先容完毕,剩下的就是在现实中行使并领略它吧! (编辑:湖南网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |