美文网首页
thinkphp6的中阶教程

thinkphp6的中阶教程

作者: wyc0859 | 来源:发表于2022-11-11 14:24 被阅读0次

    依赖注入 和 控制反转

    其实 依赖注入 和 控制反转 说的是同一件事情,只是站的角度不同而已。
    我们就拿超人和小怪兽的事情来做类比对象。

    地球受到了威胁,不断有小怪兽来想要破坏地球,每来一个小怪兽我们就需要找一个超人去对付他,一个超人肯定是不够的,因为每次来到小怪兽都是不一样的,他们所具有的能力也是不一样的。

    因此我们必须找到合适的超人去对付他,最坏的情况是每来一个小怪兽我们就要找一个或者制造一个新超人,那么来十个小怪兽,我们就要制造十个,来百个就要制造百个,来千,来万,来亿我们就要制造相应的超人,而大部分超人只能用一次。

    为了解决这个问题我们引入依赖注入和控制反转的概念,我们将超人和超能力分开,独立的超人和独立的超能力,当一个小怪兽来的时候我们找到超人,将相应的超能力给予他,让他去消灭小怪兽。

    这样的话我们只需要几个超人就好了,我们不再需要制造超人,而是研究如何制造更多更好的超能力给超人使用。

    超能力和超人不再是强依赖关系。超能力是由外部给予超人的,超人和超能力有依赖,但是这个依赖是外部给予,因此我们可以说超能力是由外部注入给他的,所以这就叫 依赖注入。

    而反过来说,超人具有何种超能力不是他内部自身控制的,而是由外部控制的,相当于将超能力具有何种功效交给了外部,外部来决定超人该有的超能力,所以超能力的控制权被由自身控制反转为外部控制,这被称为 控制反转。

    这就是关于 依赖注入 和 控制反转 的我的比较好理解的解释。它能较好的解决对象与对象之间的强耦合问题,同时也能做的按需使用按需加载。


    实例:Class A 中用到了 Class B 的对象 b,一般情况下,需要在 A 的代码中显式的 new 一个 B 的对象。
    采用依赖注入技术之后,A 的代码只需要定义一个私有的 B 对象,不需要直接 new 来获得这个对象,而是通过相关的容器控制程序来将 B 对象在外部 new 出来并注入到 A 类里的引用中。

    这样做有什么好处呢?
    解释:假如现在有 N 多个类,需要用到 Class B, 那就需要在类里面实例化 N 多次,这样对于后期的维护和管理都是不方便的,如果后期需求发生改变,那更改量有大很多。

    依赖注入的具体实现:

    image.png

    上面代码中的就是依赖注入,现在又有一个问题
    假如,现在类 b 中的 b 方法现在改名为 c 了,那 a 类里面的 a 方法中,就要将 b 类中调用的 b 方法改成 c 方法。那依旧涉及到那个问题,如果有 N 多个类要用到 B 类呢?那需要做的也是去 N 多个类中不断得去改,这样对后期得维护,需求更改都是要花费很大的成本。

    直接贴代码:


    image.png

    图中可以看到,b 类是一个接口类,c,d 继承了 b 类,接口类的具体规则。
    如果将C类中是b()改为c(),那编辑器会提示错误,由于接口必须有b函数,所以再写一个b函数即可。


    TP6中 自动进行依赖注入

    namespace app\controller;
    class Foo 
    {
        public function __construct(Bar $bar)
        {
        }
    }
    

    如果直接new的话,需要手动传入Bar对象实例

    $bar = new Bar(); 
    $foo = new Foo($bar);
    

    如果使用invoke的话,可以自动进行依赖注入。

    $foo = invoke('app\controller\Foo'); //不需要先new Bar,是自动进行依赖注入
    

    容器

    一个基本的服务容器
    先搞清楚服务的概念,不要想多了,任何一个功能,任务都可以叫做服务 service。所以说功能类对象,就是服务。
    再看容器,把这些服务装在一起,装在哪?就是一个容器。其实就是一个可以找到这些服务的一个对象。

    通常一个容器要具有绑定和解析两个操作。

    绑定,指的是将获取服务对象的方法在容器中进行注册。相当于将服务装入到了容器中。
    解析,指的是将绑定到容器中的服务从容器中提取出来,注意通常我们绑定的不是对象本身,而是生成对象的代码,因此解析时通常是执行代码来得到对象。

    # 1, 服务容器定义
    /**
     * Class Application
     * 服务容器类,类名参考Laravel
     */
    class Application
    {
    //    已绑定(注册)的服务
        private $services = [];
    
        /**
         * 绑定(注册)
         * @param $class string
         * @param $generator Closure
         */
        public function bind($class, $generator)
        {
            $this->services[$class] = $generator;
        }
    
        /**
         * 解析
         * @param $class string
         */
        public function make($class)
        {
            //return $this->services[$class]; //这样返回的是函数,不是函数结果
            return call_user_func($this->services[$class]); //这样是运行函数,返回函数结果
        }
    }
    
    # 2, 服务类示例
    /**
     * Class Kernel
     * 内核服务类
     */
    class Kernel
    {
    }
    /**
     * Class Request
     * 请求服务类
     */
    class Request
    {
    }
    
    # 3, 绑定服务到容器,通常在程序初始化阶段完成
    $app = new Application();
    $app->bind('Kernel', function() {
        return new Kernel();
    });
    $app->bind('Request', function() {
        return new Request();
    });
    
    # 4, 需要时从容器中解析
    $kernel = $app->make('Kernel');
    var_dump($kernel);
    $request = $app->make('Request');
    var_dump($request);
    

    观察上面的代码,Application 类就是服务容器类,实例化的 $app 就是服务容器对象。服务都绑定在这个容器上。
    Kernel 和 Request 就是具体的某个服务。别忘了任何功能都可以是服务。
    $app->bind (),就是将服务绑定到容器中,注意,绑定的是对象生成代码,而不是对象本身。因此绑定时,并没有去实例化对象。
    $app->make (),就是从服务容器中解析服务,其实就是调用对应类的生成代码,得到对应的服务对象。

    以上就是一个基本的服务容器。提供服务容器这种架构的目的,就是将项目中各种各样复杂多样,需要复用的功能整理到一起来管理。

    什么情况适合放入容器

    • 容器中的类并不会自动执行,而是调用时执行
    • tp6容器中已经调用过的类会自动使用单例,所以这是容器的优势

    适合放入容器,不放服务中:
    工具类(方便、并且是单例)


    TP6的服务

    就是需要在控制器之前,先完成一些配置、操作等,可放入服务中。
    因为服务的执行优先级高
    1、会先执行 自定义服务类的 register() 方法 将服务绑定到容器中
    2、再执行 自定义服务类的 boot() 启动方法,执行函数内的操作

    适合放入服务:
    微信、短信、上传、用户、管理员、配置信息等,放入容器


    中间件

    中间件主要用于拦截或过滤应用的HTTP请求,并进行必要的业务处理。

    适合中间件:
    用户、管理员、配置信息等从容器中调用对应的方法,自动单例

    跟http请求相关的放 中间件,不相关的放 服务


    请求顺序

    image.png

    相关文章

      网友评论

          本文标题:thinkphp6的中阶教程

          本文链接:https://www.haomeiwen.com/subject/lnbztdtx.html