php依赖注入与容器,Container,控制反转
依赖注入与Ioc容器
概念:
- 容器:可以理解为用来存放某个东西的物品(篮子?),存放的东西取决于你想往里面放点什么。在这里,我们是存放某个类,类的描述或者一个返回类实例的闭包函数。
- Ioc(Inversion of Control) 控制反转:可以理解为,你(用户),小红(容器)。你现在需要一把锤子,但你不想自己去造一个锤子去。你可以交给小红去处理。比如对小红说我想要一把锤子。小红会通过你给的工具名(锤子),通过自己的方法。去得到锤子的原材料(类的构造),接着在小红这里,直接造出来了一把锤子,不需要你来动手。你不需要知道这个锤子(类)所需的原材料(这里是指类的构造参数)。你通过小红(容器)而获取到了这个工具(类的实例).
- DI(Dependency Injection) 依赖注入:这和Ioc是同一种东西,但不同的是角度。例如:工具(锤子)依赖于小红(容器)去获取工具的原材料,并创建出工具(锤子).
特性:
- 减少系统之间的耦合性
- 增加代码稳定和健壮性
- 也可以理解为工厂模式的一种升级
php大神聚集地:294088839
Demo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | Class Demo1 { public $name ; public function __construct(Demo2 $demo ) { $this ->demo = $demo ; } public function Name() { $this ->name = $this ->demo->getName(); return $this ->name; } } //Demo2.php Class Demo2 { public function getName() { return "名字是xxx<br>" ; } } //正常情况下,我们是需要先实例demo1然后在demo1的构造函数内传入demo2的 //实例,这样的耦合度太高,不宜于第二次扩展 //一般情况下的手法 //直接在new Demo1时就把Demo2给new出来并传入进去 $demo = new Demo1( new Demo2()); echo $demo ->Name(); //输出名字是xxx //通过Ioc容器实现 Class Container { //存储当前类的实例 private static $instance ; //设置类不能直接new private function __construct(){} //禁止复制当前类 private function __clone(){} //获取当前类的实例 public static function _ins() { //判断成员变量是否存储实例 if ( empty (self:: $instance )) { //如果没有则存储并返回实例 self:: $instance = new static (); return self:: $instance ; } //如果存储则直接返回 return self:: $instance ; } //成员变量register存储类的实例或类的描述 private $register = []; //通过魔术方法__set和__get实现 //设置未定义的成员变量时,会经过__set public function __set( $key , $Cvalue ) { //判断是否已经存储 if ( array_key_exists ( $key , $this ->register)) { throw new Exception( "错误,已存在这一的一个类" ); } $this ->register[ $key ] = $Cvalue ; } //访问未定义的成员变量 public function __get( $key ) { //通过build动态的去获取到类的实例 return $this ->build( $this ->register[ $key ]); } //自动绑定,自动解析 public function build( $ClassName ) { //如果是匿名函数则直接返回执行后的结果 if ( $ClassName instanceof Closure) { return $ClassName ( $this ); } //通过反射获取到类的内部结构 $reflector = new ReflectionClass( $ClassName ); //判断类能不能实例化,排除掉抽象类和接口 if (! $reflector ->isInstantiable()) { throw new Exception( "对象不能实例化" ); } //获取到类的构造函数参数 $constructor = $reflector ->getConstructor(); //判断构造参数是否没有定义,如果没有,则直接返回类实例 if ( empty ( $constructor )) { return new $ClassName (); } //获取到构造函数内的参数 $params = $constructor ->getParameters(); //递归的去调用方法解析并构造参数 $dependencies = $this ->getDependencies( $params ); //创建类的实例 return $reflector ->newInstanceArgs( $dependencies ); } //解析参数 public function getDependencies( $parameters ) { //存储解析后的参数 $dependencies = []; /** foreach循环获取参数,如果是变量并有默认值就直接返回默认值,如果没有 */ foreach ( $parameters as $parameter ) { /** 通过反射获取到参数的类名,如果没有。。则直接返回默认值*/ $dependency = $parameter ->getClass(); if ( is_null ( $dependency )) { // 是变量,有默认值则设置默认值 $dependencies [] = $this ->resolveNonClass( $parameter ); } else { // 是一个类,递归解析 $dependencies [] = $this ->build( $dependency ->name); } } return $dependencies ; } public function resolveNonClass( $parameter ) { // 有默认值则返回默认值 if ( $parameter ->isDefaultValueAvailable()) { return $parameter ->getDefaultValue(); } //没有默认值就发出警告 throw new Exception( '参数没又默认值' ); } } //通过Ioc容器获取的 //实例化容器 $app = Container::_ins(); //直接依赖注入 $app ->demo1 = 'Demo1' ; $demo1 = $app ->demo1; //输出名字是xxx echo $demo1 ->Name(); |
参考资料:
世人慌慌张张,不过图碎银几两
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)