zoukankan      html  css  js  c++  java
  • 依赖注入demo

    让我们看一个例子:

    class UserProvider{
        protected $connection;
    
        public function __construct(){
            $this->connection = new Connection;
        }
    
        public function retrieveByCredentials( array $credentials ){
            $user = $this->connection
                            ->where( 'email', $credentials['email'])
                            ->where( 'password', $credentials['password'])
                            ->first();
    
            return $user;
        }
    }

    如果你要测试或者维护这个类,你必须访问数据库的实例来进行一些查询。为了避免必须这样做,你可以将此类与其他类进行 解耦 ,你有三个选项之一,可以将 Connection 类注入而不需要直接使用它。

    将组件注入类时,可以使用以下三个选项之一:

    构造方法注入

    class UserProvider{
        protected $connection;
    
        public function __construct( Connection $con ){
            $this->connection = $con;
        }
    }

    Setter 方法注入

    同样,我们也可以使用 Setter 方法注入依赖关系:

    class UserProvider{
        protected $connection;
        public function __construct(){
            ...
        }
    
        public function setConnection( Connection $con ){
            $this->connection = $con;
        }
    }

    接口注入

    interface ConnectionInjector{
        public function injectConnection( Connection $con );
    }
    
    class UserProvider implements ConnectionInjector{
        protected $connection;
    
        public function __construct(){
            ...
        }
    
        public function injectConnection( Connection $con ){
            $this->connection = $con;
        }
    }

    当一个类实现了我们的接口时,我们定义了 injectConnection 方法来解决依赖关系。

    优势

    现在,当测试我们的类时,我们可以模拟依赖类并将其作为参数传递。每个类必须专注于一个特定的任务,而不应该关心解决它们的依赖性。这样,你将拥有一个更专注和可维护的应用程序。

    ======================= 华丽的分割线 ===================
     
    目录结构:

    部分代码:

    index.php

    <?php
    
    spl_autoload_register('autoLoad', true, true);
    
    use aTestA;
    use bTestB;
    
    // 要调用的类
    $class = TestA::class;
    // 调用类中的方法
    $method = 'hello';
    
    $obj = createObj($class);
    $methodParam = parseMethod(TestA::class, $method);
    call_user_func_array([$obj, $method], $methodParam);
    
    /**
     * 根据类名 创建对象 并自动注入
     * @param string $className
     * @return object
     * @throws ReflectionException
     */
    function createObj(string $className): object
    {
        $reflction = new ReflectionClass($className);
        $paramList = parseConstructor($reflction);
        $obj = $reflction->newInstanceArgs($paramList);
        return $obj;
    }
    
    /**
     * 解析类的构造函数,返回参数列表
     * @param ReflectionClass $reflction
     * @return array
     */
    function parseConstructor(ReflectionClass $reflction): array
    {
        $paramList = [];
    
        // 如果构造方法存在,返回 object(ReflectionMethod) 反之返回 null
        $constructor = $reflction->getConstructor();
    
        if (!is_null($constructor)) {
            // 返回数组 object(ReflectionParameter) 参数列表
            $params = $constructor->getParameters();
            foreach ($params as $param) {
                // 获取参数的类型提示类,类型为 object(ReflectionClass) 若没有返回 null
                if (!is_null($param->getClass())) {
                    // 获取类名
                    $paramList[] = createObj($param->getClass()->getName());
                } else {
                    // 获取默认值
                    $paramList[] = $param->getDefaultValue();
                }
            }
        }
        return $paramList;
    }
    
    /**
     * 解析返回类的某方法参数
     *
     * @param string $className
     * @param string $methodName
     * @return array
     * @throws ReflectionException
     */
    function parseMethod(string $className, string $methodName)
    {
        $paramList = [];
        // object(ReflectionMethod)
        $reflection = new ReflectionMethod($className, $methodName);
        $params = $reflection->getParameters();
        foreach ($params as $param) {
            // 获取参数的类型提示类,类型为 object(ReflectionClass) 若没有返回 null
            if (!is_null($param->getClass())) {
                // 获取类名
                $paramList[] = createObj($param->getClass()->getName());
            } else {
                // 获取默认值
                $paramList[] = $param->getDefaultValue();
            }
        }
        return $paramList;
    }
    
    function autoLoad($className)
    {
        require_once __DIR__.'/'.$className.'.php';
    }
    
    function dump($data)
    {
        echo '<pre>';
        var_dump($data);
        exit();
    }

    class TestA

    <?php
    
    namespace a;
    
    use bTestB;
    use faceTest;
    
    class TestA implements Test
    {
        const VERSION = 'A';
        public $name;
        public $objB;
    
        public function __construct(TestB $b, $name='aaa')
        {
            $this->objB = $b;
            $this->name = $name;
        }
    
        public function hello(TestB $b)
        {
            var_dump($b);
        }
    }

    class TestB

    <?php
    
    namespace b;
    
    use faceTest;
    use cTestC;
    
    class TestB implements Test
    {
        const VERSION = 'B';
        public $name;
        public $objC;
    
        public function __construct($name = 'bbb', TestC $c)
        {
            $this->objC = $c;
            $this->name = $name;
        }
    }

    class TestC

    <?php
    
    namespace c;
    
    use faceTest;
    
    class TestC implements Test
    {
        const VERSION = 'C';
    
        public function __construct($name = 'ccc')
        {
        }
    }

    class Face

    <?php
    
    namespace face;
    
    interface Test
    {
        
    }

    另一个实例:

    <?php
    
    class Point
    {
        public $x;
        public $y;
    
        public function __construct($x = 0, $y = 0)
        {
            $this->x = $x;
            $this->y = $y;
        }
    }
    
    class Circle
    {
        protected $radius;
    
        protected $center;
    
        public function __construct(Point $point, $raduis = 1)
        {
            $this->center = $point;
            $this->radius = $raduis;
        }
    
        public function printCenter()
        {
            printf('center coordinate is (%d, %d)', $this->center->x, $this->center->y);
        }
    
        public function area()
        {
            return 3.14 * pow($this->radius, 2);
        }
    }
    
    class DI
    {
        public function make($className)
        {
            if (class_exists($className)) {
                $reflectionClass = new ReflectionClass($className);
                $constructor = $reflectionClass->getConstructor();
                if (is_null($constructor)) {
                    return $reflectionClass->newInstance();
                } else {
                    $parameters = $constructor->getParameters();
                    $dependencies = $this->getDependencies($parameters);
    
                    return $reflectionClass->newInstanceArgs($dependencies);
                }
    
            } else {
                printf('%s', 'class is not exist');
            }
        }
    
        // 依赖解析
        protected function getDependencies($parameters)
        {
            $dependencies = [];
            foreach ($parameters as $parameter) {
                $dependency = $parameter->getClass();
                if (is_null($dependency)) {
                    if ($parameter->isDefaultValueAvailable()) {
                        $dependencies[] = $parameter->getDefaultValue();
                    } else {
                        //不是可选参数的为了简单直接赋值为字符串0
                        //针对构造方法的必须参数这个情况
                        //laravel是通过service provider注册closure到IocContainer,
                        //在closure里可以通过return new Class($param1, $param2)来返回类的实例
                        //然后在make时回调这个closure即可解析出对象
                        //具体细节我会在另一篇文章里面描述
                        $dependencies[] = '0';
                    }
                } else {
                    //递归解析出依赖类的对象
                    $dependencies[] = $this->make($parameter->getClass()->name);
                }
            }
    
            return $dependencies;
        }
    }
    
    
    echo '<pre>';
    $obj = (new DI)->make(Circle::class);
    $obj->printCenter();

    参考文档:

    https://juejin.im/post/5cac58ecf265da03a85a9f9a#heading-0

    https://juejin.im/post/5ae292e7518825673446c1fe

    https://www.insp.top/learn-laravel-container

    https://github.com/kevinyan815/Learning_Laravel_Kernel/blob/master/articles/reflection.md

  • 相关阅读:
    将mysql数据库的数据导出做成excl表格通过邮件发送附件发给指定人
    监听服务端口及邮件报警脚本
    ubantu下docker安装
    python 邮件报警
    3、.net core 部署到IIS
    1、Ubuntu 16.04 安装.net core
    解决asp.net mvc的跨域请求问题
    Jquery常用方法汇总(转)
    mongodb Helper
    数据库CTE递归查询
  • 原文地址:https://www.cnblogs.com/cshaptx4869/p/10688431.html
Copyright © 2011-2022 走看看