什么是依赖注入?
IOC:英文全称:Inversion of Control,中文名称:控制反转,它还有个名字叫依赖注入(Dependency Injection,简称DI)。
当一个类的实例需要另一个类的实例协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。而采用依赖注入的方式,创建被调用者的工作不再由调用者来完成,因此叫控制反转,创建被调用者的实例的工作由IOC容器来完成,然后注入调用者,因此也称为依赖注入。
举个简单的例子:
(1)原始社会里,几乎没有社会分工。需要斧子的人(调用者)只能自己去磨一把斧子(被调用者)。
(2)进入工业社会,工厂出现。斧子不再由普通人完成,而在工厂里被生产出来,此时需要斧子的人(调用者)找到工厂,购买斧子,无须关心斧子的制造过程。
(3)进入“按需分配”社会,需要斧子的人不需要找到工厂,坐在家里发出一个简单指令:需要斧子。斧子就自然出现在他面前。
第一种情况下,实例的调用者创建被调用的实例,必然要求被调用的类出现在调用者的代码里。无法实现二者之间的松耦合。
第二种情况下,调用者无须关心被调用者具体实现过程,只需要找到符合某种标准(接口)的实例,即可使用。此时调用的代码面向接口编程,可以让调用者和被调用者解耦,这也是工厂模式大量使用的原因。但调用者需要自己定位工厂,调用者与特定工厂耦合在一起。
第三种情况下,调用者无须自己定位工厂,程序运行到需要被调用者时,依赖注入容器自动提供被调用者实例。事实上,调用者和被调用者都处于依赖注入容器的管理下,二者之间的依赖关系由依赖注入容器提供。因此调用者与被调用者的耦合度进一步降低,这使得应用更加容易维护,这就是依赖注入所要达到的目的。
用php实现一个轻量的依赖注入容器
首先我们创建一个类,看起来是这样的:
<?php class Di{ protected $_service = []; public function set($name, $definition) { $this->_service[$name] = $definition; } public function get($name) { if (isset($this->_service[$name])) { $definition = $this->service[$name]; } else { throw new Exception("Service '" . name . "' wasn't found in the dependency injection container"); } if (is_object($definition)) { $instance = call_user_func($definition); } return $instance; }}
现在我们已经有了一个简单的类,包含一个属性和两个方法。假设我们现在有两个类,redisDB和cache,redisDB提供一个redis数据库的操作,cache负责缓存功能的实现并且依赖于redisDB。
class redisDB{ protected $_di; protected $_options; public function __construct($options = null) { $this->_options = $options; } public function setDI($di) { $this->_di = $di; } public function find($key, $lifetime) { // code } public function save($key, $value, $lifetime) { // code } public function delete($key) { // code }}
在这个类中我们简单实现了redis的查询、保存和删除。你可能会有疑问,另外一个方法setDi是做什么的。待我继续为你讲解。另一个类和当前这个类结构很像:
class cache{ protected $_di; protected $_options; protected $_connect; public function __construct($options = null) { $this->_options = $options; } public function setDI($di) { $this->_di = $di; } protected function _connect() { $options = $this->_options; if (isset($options['connect'])) { $service = $options['connect']; } else { $service = 'redis'; } return $this->_di->get($service); } public function get($key, $lifetime) { $connect = $this->_connect; if (!is_object($connect)) { $connect = $this->_connect() $this->_connect = $connect; } // code ... return $connect->find($key, $lifetime); } public function save($key, $value, $lifetime) { $connect = $this->_connect; if (!is_object($connect)) { $connect = $this->_connect() $this->_connect = $connect; } // code ... return $connect->save($key, $lifetime); } public function delete($key) { $connect = $this->_connect; if (!is_object($connect)) { $connect = $this->_connect() $this->_connect = $connect; } // code ... $connect->delete($key, $lifetime); }}
现在我们就当已经实现了redisDB和cache这两个组件,具体的细节这里就先不做讨论了,来看看如何使用使用吧。首先需要将两个组件注入到容器中:
<?php $di = new Di(); $di->set('redis', function() { return new redisDB([ 'host' => '127.0.0.1', 'port' => 6379 ]); }); $di->set('cache', function() use ($di) { $cache = new cache([ 'connect' => 'redis' ]); $cache->setDi($di); return $cache; }); // 然后在任何你想使用cache的地方 $cache = $di->get('cache'); $cache->get('key'); // 获取缓存数据 $cache->save('key', 'value', 'lifetime'); // 保存数据 $cache->delete('key'); // 删除数据
到这里你可能会觉得这样以来反而有点繁琐了。cache和redisDB的结构如此之像,完全可以把redis写到cache中而没必要单独分离出来?但是你想过没有,有些数据及时性没那么高而且数量比较大,用redis有点不合适,mongodb是更好的选择;有些数据更新频率更慢,对查询速度也没要求,直接写入文件保存到硬盘可能更为合适;再或者,你的客户觉得redis运维难度有点大,让你给他换成memcache... 这就是为什么把它分离出来了。然后,继续改进代码:
interface BackendInterface { public function find($key, $lifetime); public function save($key, $value, $lifetime); public function delete($key);}class redisDB implements BackendInterface{ public function find($key, $lifetime) { } public function save($key, $value, $lifetime) { } public function delete($key) { }}class mongoDB implements BackendInterface{ public function find($key, $lifetime) { } public function save($key, $value, $lifetime) { } public function delete($key) { }}class file implements BackendInterface{ public function find($key, $lifetime) { } public function save($key, $value, $lifetime) { } public function delete($key) { }}$di = new Di();// redis$di->set('redis', function() { return new redisDB([ 'host' => '127.0.0.1', 'port' => 6379 ]);});// mongodb$di->set('mongo', function() { return new mongoDB([ 'host' => '127.0.0.1', 'port' => 12707 ]);});// file$di->set('file', function() { return new file([ 'path' => 'path' ]);});// save at redis$di->set('fastCache', function() use ($di) { $cache = new cache([ 'connect' => 'redis' ]); $cache->setDi($di); return $cache;});// save at mongodb$di->set('cache', function() use ($di) { $cache = new cache([ 'connect' => 'mongo' ]); $cache->setDi($di); return $cache;});// save at file$di->set('slowCache', function() use ($di) { $cache = new cache([ 'connect' => 'file' ]); $cache->setDi($di); return $cache;});// 然后在任何你想使用cache的地方 $cache = $di->get('cache');
我们新增加了一个接口BackendInterface,规定了redisDB,mongoDB,file这三个类必须实现这个接口所要求的功能,至于其他锦上添花的功能,随你怎么发挥。而cache的代码,好像没有变,因为cache不需要关心数据是怎么存入数据库或者文件中。而cache的调用者,也不需要关心cache具体是怎么实现的,只要根据接口实现相应的方法就行了。多人协作你会更加受益,你们只需要商定好接口,然后分别实现就行了。
这就是依赖注入的魅力所在了,虽然看似如此简单。
以上代码还可以继续改进,直到你认为无可挑剔为止。比如,redis服务在一个请求中会调用多次,而每次调用都会重新创建,这将有损性能。只需扩展一下DI容器就好怎加一个参数或者怎加一个方法,随你。
class Di{ protected $_service = []; protected $_sharedService = []; public function set($name, $definition, $shared = false) { if ($shared) { $this->_sharedService[$name] = $definition; } else { $this->_service[$name] = $definition; } } public function get($name) { if (isset($this->_service[$name])) { $definition = $this->service[$name]; } else if ($this->_sharedService[$name]) { $definition = $this->_sharedService[$name]; } else { throw new Exception("Service '" . name . "' wasn't found in the dependency injection container"); } ... }
这样以来,如果某个服务在一次请求中要调用多次,你就可以将shared属性设置为true,以减少不必要的浪费。如果你觉得每次在注入时都要setDi有点繁琐,想让他自动setDi,那可以这么做:
interface DiAwareInterface{ public function setDI($di); public function getDI();}class Di{ protected $service; public function set($name, $definition) { $this->service[$name] = $definition; } public function get($name) { ... if (is_object($definition)) { $instance = call_user_func($definition); } // 如果实现了DiAwareInterface这个接口,自动注入 if (is_object($instance)) { if ($instance instanceof DiAwareInterface) { $instance->setDI($this); } } return $instance; }}class redisDB implements BackendInterface, DiAwareInterface{ public function find($key, $lifetime) { } public function save($key, $value, $lifetime) { } public function delete($key) { }}
然后,就可以这样:
$di->set('cache', function() { return new cache([ 'connect' => 'mongo' ]);});
我们现在所实现的这个DI容器还很简陋,还不支持复杂的注入,你可以继续完善它。
不过,通过这些代码你已经了解什么是依赖在注入了,你可以将这种思想应用到你的项目中,或者着手开发你自己的框架。如果想继续深入学习的话,烦请 click Scene。
完。

PHP在现代编程中仍然是一个强大且广泛使用的工具,尤其在web开发领域。1)PHP易用且与数据库集成无缝,是许多开发者的首选。2)它支持动态内容生成和面向对象编程,适合快速创建和维护网站。3)PHP的性能可以通过缓存和优化数据库查询来提升,其广泛的社区和丰富生态系统使其在当今技术栈中仍具重要地位。

在PHP中,弱引用是通过WeakReference类实现的,不会阻止垃圾回收器回收对象。弱引用适用于缓存系统和事件监听器等场景,需注意其不能保证对象存活,且垃圾回收可能延迟。

\_\_invoke方法允许对象像函数一样被调用。1.定义\_\_invoke方法使对象可被调用。2.使用$obj(...)语法时,PHP会执行\_\_invoke方法。3.适用于日志记录和计算器等场景,提高代码灵活性和可读性。

Fibers在PHP8.1中引入,提升了并发处理能力。1)Fibers是一种轻量级的并发模型,类似于协程。2)它们允许开发者手动控制任务的执行流,适合处理I/O密集型任务。3)使用Fibers可以编写更高效、响应性更强的代码。

PHP社区提供了丰富的资源和支持,帮助开发者成长。1)资源包括官方文档、教程、博客和开源项目如Laravel和Symfony。2)支持可以通过StackOverflow、Reddit和Slack频道获得。3)开发动态可以通过关注RFC了解。4)融入社区可以通过积极参与、贡献代码和学习分享来实现。

PHP和Python各有优势,选择应基于项目需求。1.PHP适合web开发,语法简单,执行效率高。2.Python适用于数据科学和机器学习,语法简洁,库丰富。

PHP不是在消亡,而是在不断适应和进化。1)PHP从1994年起经历多次版本迭代,适应新技术趋势。2)目前广泛应用于电子商务、内容管理系统等领域。3)PHP8引入JIT编译器等功能,提升性能和现代化。4)使用OPcache和遵循PSR-12标准可优化性能和代码质量。

PHP的未来将通过适应新技术趋势和引入创新特性来实现:1)适应云计算、容器化和微服务架构,支持Docker和Kubernetes;2)引入JIT编译器和枚举类型,提升性能和数据处理效率;3)持续优化性能和推广最佳实践。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能

记事本++7.3.1
好用且免费的代码编辑器

SecLists
SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

MinGW - 适用于 Windows 的极简 GNU
这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境