php依赖注入与容器,Container,控制反转

  • 2020 年 4 月 12 日
  • 筆記

依赖注入与Ioc容器

概念:

  1. 容器:可以理解为用来存放某个东西的物品(篮子?),存放的东西取决于你想往里面放点什么。在这里,我们是存放某个类,类的描述或者一个返回类实例的闭包函数。
  2. Ioc(Inversion of Control) 控制反转:可以理解为,你(用户),小红(容器)。你现在需要一把锤子,但你不想自己去造一个锤子去。你可以交给小红去处理。比如对小红说我想要一把锤子。小红会通过你给的工具名(锤子),通过自己的方法。去得到锤子的原材料(类的构造),接着在小红这里,直接造出来了一把锤子,不需要你来动手。你不需要知道这个锤子(类)所需的原材料(这里是指类的构造参数)。你通过小红(容器)而获取到了这个工具(类的实例).
  3. DI(Dependency Injection) 依赖注入:这和Ioc是同一种东西,但不同的是角度。例如:工具(锤子)依赖于小红(容器)去获取工具的原材料,并创建出工具(锤子).

特性:

  1. 减少系统之间的耦合性
  2. 增加代码稳定和健壮性
  3. 也可以理解为工厂模式的一种升级

php大神聚集地:294088839

Demo:

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();  

  

参考资料: