快速理解Laravel容器(IOC、DI、Provider、Contract)

源码理解思维的提升

分享一些个人见解。
Laravel里面的某些概念,就像魔术一样,看起来很厉害,当知道魔术怎么变的,就会认为也不过如此。所以不必感觉Laravel里有些概念难以理解。

应当抛除被框架约束思维的枷锁,用PHP设计的角度去思考,关注大概,而不是在在框架层面逐行磨叽。毕竟源码那么多,越是底层变量越多,越抽象,频繁的调用和变量指向,看到天亮也看不明白。

PHP设计的角度去思考怎么理解:
对于Laravel源码,代码很优雅,很工程化,很正规,方便扩展,这是优点,坏处就是抽象,好比垃圾桶里的垃圾都要摆放好,太过于工程化的设计,会给写松散业务逻辑的开发者带来困惑。
举个例子,Laravel里面的很多契约,好多都是一个空interface,放到了vendor下,真的是可要可不要,也确实有不少人说泰勒·奥特威尔就是炫技。
所以迟早要被绕晕,必须抛开Laravel里面的各种细节,用不需要太工程化PHP代码去尝试实现它,实现的过程中会遇到各种优化或者是问题,然后逐步优化。

被框架约束思维的枷锁怎么理解:
框架发展了这么久,源码的累加,不是让从头看的,而是遇到了一类问题,需要追加代码,是一个演进的过程,通过出现的问题或要优化的需求作为驱动来实现代码(书写顺序),而不是通过代码的作用反推解决的问题(反向反了很难看懂源码,看不懂干啥的,原因就是这里),换句话说,1+1=?是问题,2是结果。方向反了只有结果2,怎么推理出问题是1+1,还是2+0,既然不知道问题,又怎么知道2的意义是啥,那个地方要用?

易经思维:像各国人描述乌龟,得学很长时间的各国语言(源码),随手画一个乌龟(思维),全世界的人多能看懂。
要发掘的不是源码这样写我怎么看懂,而是这样写解决了什么问题。

通俗的演进示例

第1步:直接上第一版代码,Test类实现查询功能,于是调用了MySQL类的select方法。

class MySQL {
    public function select() {
        echo '执行查询操作';
    }
}


class Test {
    private $driver;
    public function __construct() {
        $this->driver = new MySQL();
    }

    public function search() {
        return $this->driver->select();
    }
}

$crud = new Test();
$crud->search();

第2步:但是如果以后有了Oracle,PostgreSQL怎么办?,代码内部的依赖写死了。
所以类的内部要写活,将内部的依赖,反转到外边,控制权由类的外部控制,通过构造方法传参实现,这就是控制反转,方便代码解耦。

<?php

class MySQL {
    public function select() {
        echo '执行查询操作';
    }
}


class Test {
    private $driver;
    public function __construct($driver) {
        $this->driver = $driver;
    }

    public function search() {
        return $this->driver->select();
    }
}

$crud = new Test(new MySQL);
$crud->search();

第3步:但是随着项目的复杂,类越来越多,对象到处都是,得有一个管事的领导人,进行统一管理,于是容器出现了。
容器至少需要两个方法,bind和make,bind用于存储,相当于领导人管辖的地域,分配一块地区,让这些容器有地方生存。make顾名思义就是制造,创建出来的这些容器(就是实例化的过程)让他们做事。
bind方法一般不会直接实例化对象,因为可能用不上,但是代码流程走到哪里了,存是得存起来,防止后期用得上,这很好理解,就好比Laravel的Cache模块,不是每次请求都一定能用得上,但是它仍旧会被预先加载。

class Container {
    private $container = [];

    public function bind($name, $class) {
        return $this->container[$name] = $class;
    }

    public function make($name) {
        $class = $this->container[$name] ?? null;
        if(! $class) {
            return null;
        }

        return new $class();
    }
}

第4步,整合到第2步的代码当中。
看起来简单东西搞复杂了,确实。但是框架中,对于大量的类和对象的管理却很有用,结合框架的反射,以及其它机制,是能够让类的管理变的更优雅的,
没有容器情况下的例子:
要狗肉账,张三问李四要债,李四说债务转移给王五了去问王五要,王五说债务转移给赵六了你去问他要,彼此踢皮球的环节耦合度太高,互相依赖。
在这里插入图片描述

有容器情况下的例子:
相当于直接起诉到法院,一步到位,不管你是谁。我通过法院的管事人,去解决问题。
在这里插入图片描述

<?php

class MySQL {
    public function select() {
        echo '执行查询操作';
    }
}


class Test {
    private $driver;
    public function __construct($driver) {
        $this->driver = $driver;
    }

    public function search() {
        return $this->driver->select();
    }
}


class Container {
    private $container = [];

    public function bind($name, $class) {
        return $this->container[$name] = $class;
    }

    public function make($name) {
        $class = $this->container[$name] ?? null;
        if(! $class) {
            return null;
        }

        return new $class();
    }
}

$container = new Container();
$container->bind('MySQL', MySQL::class);


$crud = new Test($container->make('MySQL'));
$crud->search();

Laravel容器

laravel容器的源码在vendor/laravel/framework/src/Illuminate/Container/Container.php
Laravel核心概念,类比成一个存放各类对象的数组,贯穿了整个Laravel流程,用于降低耦合度,管理并存储各模块的类与对象。
一个框架提供了很多模块,就需要很多对象,这些对象需要统一管理,自动实例化,以及类类型的形参自动实例化的反射机制,于是有了容器。

IOC(控制反转)

类内部的依赖,反转到类外边,控制权由类的外部控制,通过构造方法传参实现,这就是控制反转,方便代码解耦。
上文示例有说明。

DI(依赖注入)

依赖注入是控制反转的一种实现方式
依赖注入函数内部传参,有助于提高代码的模块化、可测试性和灵活性,通俗讲就是按(开发者)需加载。
如下,UserController依赖于UserService类。在控制器的构造函数中注入了 UserService,而不是在 UserController 中实例化UserService的实例。Laravel的服务容器的反射机制会自动解析这个依赖关系。

class UserController extends Controller {
    protected $userService;
    public function __construct(UserService $userService) {
        $this->userService = $userService;
    }
}

Contract(契约)

看了容器的源码,发现有用到,顺便提一嘴。
听起来高大上,移除Laravel框架高大上的思维枷锁,也就一堆接口,用于规范化代码,实现大体布局,子类用它实现细节,没什么神奇的高科技。

Provider(服务提供者)

多个服务提供者组成一个容器,新建的provider需要再config/app.php中声明。
用于注册应用程序的服务、绑定依赖关系以及其它初始化操作,真用到的时候就去实现,没那么严谨。
先调用register方法,然后会去调用boot方法。

这个在安装某些插件的时候回去用这个,composer包里的代码,不足以支撑当前的应用场景,所以需要做一些配置,然后就用到了Provider。

app()、resolve()

我看到前公司技术老大经常这样用,以我目前的认知来看,这和直接用命名空间的逻辑去调用一个类没什么区别。

  • app(); 返回服务容器实例,参数可传递类名,表示调用那个类。
  • resolve();app()的别名, 函数使用服务容器解析给定名称的类或接口的实例,参数可传递类名,表示调用那个类。

扩展阅读

浅谈PHP框架中类成员方法的类类型形参是怎么利用ReflectionClass反射类自动实例化的(应该是全网首发)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/570762.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【ContextCapture建模精品教程】PhotoScan空三成果导入ContextCapture建模教程

【ContextCapture建模精品教程】PhotoScan空三成果导入ContextCapture建模教程 文章目录 前言一、PhotoScan软件空三解算二、ContextCapture软件操作总结前言 ContextCapture是一款行业应用广的三维建模的软件,但是ContextCapture处理的空三能力比较弱,导致出现后期模型效果…

Golang流程控制语句

文章目录 顺序控制分支控制if语句switch语句 循环控制for循环语句 跳转控制break语句continue语句goto语句return语句 顺序控制 顺序控制 默认情况下&#xff0c;Go代码执行顺序是按照从上到下依次执行的&#xff0c;这种按照顺序执行的代码就叫做顺序语句。如下&#xff1a; …

Linux 学习之路 -- 进程篇 -- 进程控制

目录 一、进程终止 <1>使用语言和系统自带的方法&#xff0c;进行转换 <2>自定义错误码 <3>小结&#xff1a; <2>两个接口exit / _exit 二、进程等待 <1>简单了解 <2>wait调用 <3>waitpid调用 <4>status <1>W…

第十四章大数据和数据科学4分

14.1 引言 14.1.3 科学理念 1.数据科学 数据科学将数据挖掘、统计分析和机器学习与数据集成整合&#xff0c;结合数据建模能力&#xff0c;去构建预测模型、探索数据内容模式。 数据科学依赖于&#xff1a; 1&#xff09;丰富的数据源。具有能够展示隐藏在组织或客户行为中不…

顺序表的应用-通讯录

顺序表的应用-通讯录 1.操作2.功能要求2.1.功能要求2.2.思路小结2.3.文件梳理2.4.代码实现"SeqList.h""Contact.h""SeqList.c""Contact.c""test.c" 1.操作 链接: 顺序表专题 这篇文章介绍了顺序表的概念与基本操作。 本文将…

什么是 GitHub Wiki 以及如何使用它?

GitHub Wiki 是你项目文档的一个很好的地方。你可以使用 wiki 来创建、管理和托管你的存储库的文档&#xff0c;以便其他人可以使用并为你的项目做出贡献。 GitHub Wiki 很容易开始使用&#xff0c;无需安装任何其他软件。最好的部分是 wiki 与你的 GitHub 存储库集成在一起。…

《九》Qt各种对话框之QColorDialog

前言 QColorDialog类继承于QDialog&#xff0c;是一个设计用来选择颜色的对话框部件。 QColorDialog 在介绍 QColorDialog 之前&#xff0c;我们先简单介绍一下 QColor 类。QColor 类用于表示颜色&#xff0c;支持 RGB&#xff08;红绿蓝&#xff09;三原色表示&#xff0c;也…

【C++】日期计算机

个人主页&#xff1a;救赎小恶魔 欢迎大家来到小恶魔频道 好久不见&#xff0c;甚是想念 今天我们要讲述的是一个日期类计算机的代码实现 引言&#xff1a; 我们日常生活中可能会有一个烦恼。 今天几月几号&#xff1f;过n天后又是几月几号&#xff1f;某年某月某天和x年…

PE文件的导入表,动态链接库中的函数应该如何导入

导入地址表IAT IAT保存的内容与windos操作系统的核心进程、内存、DLL结构有关。IAT是一种表格&#xff0c;用来记录程序正在使用哪些库中的哪些函数。 动态链接库(DLL) 常见的kernel.dll就是一个非常重要的动态链接库&#xff0c;其中包含了运行程序时需要使用到的函数&…

文件操作(1)

为什么使⽤⽂件&#xff1f; 如果没有⽂件&#xff0c;我们写的程序的数据是存储在电脑的内存中&#xff0c;如果程序退出&#xff0c;内存回收&#xff0c;数据就丢失了&#xff0c;等再次运⾏程序&#xff0c;是看不到上次程序的数据的&#xff0c;如果要将数据进⾏持久化的…

再谈C语言——理解指针(四)

assert断⾔ assert.h 头⽂件定义了宏 assert() &#xff0c;⽤于在运⾏时确保程序符合指定条件&#xff0c;如果不符合&#xff0c;就报错终⽌运⾏。这个宏常常被称为“断⾔”。 assert(p ! NULL); 上⾯代码在程序运⾏到这⼀⾏语句时&#xff0c;验证变量 p 是否等于 NULL 。…

JavaScript 数学对象 Math

Math对象其实就是数学对象&#xff0c;它给我们提供了各种各样的数学功能。 <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>首页</title> </head><body><script type"text/javascript"&g…

linux的“>”和“>>”

在Linux中&#xff0c;>和>>都是用于文件重定向的操作符&#xff0c;它们用于将命令的输出发送到文件中。 > 用于创建一个新文件或覆盖现有文件的内容。当你执行一个如 command > file.txt 的命令时&#xff0c;如果 file.txt 文件存在&#xff0c;它的内容将被…

【uniapp】引入uni-ui组件库

&#xff08;1&#xff09;新建项目的时候选择 uni-ui项目 &#xff08;2&#xff09;已经创建好的项目去官网单独安装 跳转单独安装组件 https://uniapp.dcloud.net.cn/component/uniui/quickstart.html#%E9%80%9A%E8%BF%87-uni-modules-%E5%8D%95%E7%8B%AC%E5%AE%89%E8%A3%8…

YOLOv9训练损失、精度、mAP绘图功能 | 支持多结果对比,多结果绘在一个图片(消融实验、科研必备)

一、本文介绍 本文给大家带来的是YOLOv9系列的绘图功能&#xff0c;我将向大家介绍YOLO系列的绘图功能。我们在进行实验时&#xff0c;经常需要比较多个结果&#xff0c;针对这一问题&#xff0c;我写了点代码来解决这个问题&#xff0c;它可以根据训练结果绘制损失(loss)和mA…

VBA技术资料MF144:将PDF首页作为对象插入工作表

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

【C++】如何用C++写一个日期计算器

目录 前言 代码的布局 设计数据 方法声明 方法的实现 获取某年某月的天数 *全缺省的构造函数 * 拷贝构造函数 *赋值运算符重载 *析构函数 日期天数 日期天数 日期-天数 日期-天数 前置 后置 后置-- 前置-- 实现比较大小运算符重载思路 >运算符重载 运算…

盲人餐厅点餐:科技之光照亮餐桌上的美食之旅

在现代社会&#xff0c;餐厅不仅是满足口腹之欲的场所&#xff0c;更是一个社交、放松的重要空间。然而&#xff0c;对于视障人士而言&#xff0c;盲人餐厅点餐这一日常行为&#xff0c;却往往伴随着诸多不便与挑战。幸运的是&#xff0c;科技的革新正为这一群体带来前所未有的…

递归神经网络(RNN)在AI去衣技术中的深度应用

在人工智能&#xff08;AI&#xff09;技术飞速发展的今天&#xff0c;图像处理和计算机视觉领域不断取得新的突破。其中&#xff0c;AI去衣技术作为一个具有挑战性的研究方向&#xff0c;引起了广大研究者和公众的关注。递归神经网络&#xff08;RNN&#xff09;作为深度学习的…

【linux】编译器使用

目录 1. gcc &#xff0c;g 编译器使用 a. 有关gcc的指令&#xff08;g同理&#xff09; 2. .o 文件和库的链接方式 a. 链接方式 b. 动态库 和 静态库 优缺点对比 c. debug 版本 和 release 版本 1. gcc &#xff0c;g 编译器使用 a. 有关gcc的指令&#xff08;g同理&…