https://learnku.com/articles/19195

 
 

博客 / Laravel / 11个月前 / 9415 / 44 / 更新于 2个月前 / 1 个改进

 
 

DI

DI 就是常说的依赖注入,那么究竟什么是依赖注入呢?

打个比方,电脑(非笔记本哈)需要键盘和鼠标我们才能进行操作,这个‘需要’换句话说就是‘依赖’键盘和鼠标。

那么,相应的,一个类需要另一个类才能进行作业,那么这也就是依赖。

看一段代码:


class Computer {
protected $keyboard; public function __construct() {
$this->$keyboard = new Keyboard();
}
} 这里的Computer类依赖了键盘类。

好,既然我们已经知道了什么是依赖,那么什么是注入呢?

我们改造一下上面的代码:

     class Computer {
protected $keyboard; public function __construct(Keyboard $keyboard) {
$this->$keyboard = $keyboard;
}
} $computer = new Computer(new Keyboard()); 这里的Computer类依赖注入了Keyboard类。

关于依赖注入,我的理解是:

所需要的类通过参数的形式传入的就是依赖注入。

理解了依赖注入,我们可以接着理解 IOC。

 

IOC

IOC 是什么呢?

中文叫控制反转。啥意思呢? 这个看明白了 DI 后就能很容易的理解了。

通过 DI 我们可以看到,一个类所需要的依赖类是由我们主动实例化后传入类中的。

控制反转和这个有什么关系呢?

控制反转意思是说将依赖类的控制权交出去,由主动变为被动。

看一段 laravel 代码:

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class SessionController extends Controller
{ public function login(Request $request)
{
//这就是IOC,我们不需要主动传入类了一切由laravel去实现
}
}

看到这你可能有疑问了,这是怎么实现的呢?

这就是靠服务容器了,请往下接着看。

 

服务容器

看了很多文章,我一致认为服务容器就是一种设计模式。

它的目的就是解耦依赖。

它有点类似于我前面说的《享元模式》。区别在于服务容器解决了所有依赖的实现。

这里我们再从头至尾的看一遍,怎么一步步演化出服务容器。

依然是电脑的例子,我们知道电脑依赖键盘鼠标,可是键盘鼠标也有很多种呀。

先看一个最原始的代码例子:


class Computer {
protected $keyboard; public function __construct($type = null) { switch($type) {
case 'common':
$this->keyboard = new CommonKeyboard();
case 'awesome':
$this->keyboard = new AweSomeKeyboard();
default:
$this->keyboard = new Keyboard();
} }
}

或许你一眼就看出了问题在哪。

如果我们又要增加一钟键盘,那我们又得对这个类进行修改。这样下去,这个类会变得庞大且耦合程度过高。

那么我们可以怎么修改呢?

  • 工厂模式

这样我们可以避免直接的修改 Computer 类。

    简单工厂
class Factory { public static function getInstance($type){
switch($type) {
case 'common':
$this->keyboard = new CommonKeyboard();
break;
case 'awesome':
$this->keyboard = new AweSomeKeyboard();
break;
default:
$this->keyboard = new Keyboard();
break;
}
}
} class Computer {
protected $keyboard; public function __construct($type == null) {
$this->keyboard = Factory::getInstance($type);
}
}

这样使用简单工厂模式后,我们后续的修改可以不用对 Computer 类进行操作而只要修改工厂类就行了。这就相当于对 Computer 类进行了解耦。

Computer 类虽不在依赖那些键盘类了,但是却变为依赖工厂类了。

后续添加新类型的键盘就必须对工厂类进行修改。

所以这个工厂类还不能很好的满足要求,我们知道电脑对键盘的接口都是一致的,键盘必须实现这一接口才能被电脑识别,那我们对 Computer 和 Keyboard 类进行修改。

  • DI(依赖注入)
    interface Board {
public function type();
} class CommonBoard implements Board {
public function type(){
echo '普通键盘';
}
} class MechanicalKeyboard implements Board {
public function type(){
echo '机械键盘';
}
} class Computer {
protected $keyboard; public function __construct (Board $keyboard) {
$this->keyboard = $keyboard;
}
} $computer = new Computer(new MechanialKeyBoard());

可是这样也有问题,如果我们后续对这台电脑使用的键盘不满意要进行替换呢? 我们又回到原点了,必须去修改传入的键盘类。

能不能做成可配置的呢?

  • IOC 服务容器(超级工厂)
class Container
{
protected $binds; protected $instances; public function bind($abstract, $concrete)
{
if ($concrete instanceof Closure) {
$this->binds[$abstract] = $concrete;
} else {
$this->instances[$abstract] = $concrete;
}
} public function make($abstract, $parameters = [])
{
if (isset($this->instances[$abstract])) {
return $this->instances[$abstract];
} array_unshift($parameters, $this); return call_user_func_array($this->binds[$abstract], $parameters);
}
}

这就是一个简单的 IOC 服务容器。

这个怎么解决我们上述的问题呢?

    $container = new Container;

    $container->bind('Board', function($container){
return new CommonBoard;
}); $container->bind('Computer',function($container,$module){
return new Computer($container->make($module));
}); $computer = $container->make('Computer',['Board']);

这里生产出来的 Computer 类就是一个使用普通键盘的电脑类了。

解释一下代码:

    bind(name,function($container){
return new Name;
}) 这里的name和Name之间的关系是:
当我需要name类的时候你就给我实例化Name类。 make(name)方法是对name进行生产返回一个实例。

如果我们要更换键盘怎么办呢?

    $container->bind('Board', function($container){
return new MechanicalBoard;
}); $container->bind('Computer',function($container,$module){
return new Computer($container->make($module));
}); $computer = $container->make('Computer',['Board']);

只要对 bind 绑定的 Board 类的实现进行修改,我们就可以很容易替换掉键盘了。这就是一个服务容器。

对服务容器进行一个理解:

容器就是一个装东西的,好比碗。而服务就是这个碗要装的饭呀,菜呀,等等东西。当我们需要饭时,我们就能从这个碗里拿到。如果你想在饭里加点菜(也就是饭依赖注入了菜),我们从碗里直接拿饭就可以了,而这些依赖都由容器解决了(这也就是控制反转)。

我们需要做的就是对提供的服务进行维护。

我们看一段真实的在 laravel 框架上能跑的代码:

代码

当然 laravel 框架的服务容器比这里的要复杂很多了,但我们明白了它的使用目的以及使用场景就不难去入手 laravel 了。

PS:你的赞是我创作的动力!

为什么同是 9 年义务教育别人就那么优秀?

想知道请关注订阅号:Buger(关注送 laravel,linux,nginx 等学习资料!!!)

回复 ' 学习 ',推荐你 2 本书。

三海

本帖由系统于 11个月前 自动加精
举报

laravel自定义门面的更多相关文章

  1. Laravel5 创建自定义门面(Facade)

    门面为应用服务容器中的绑定类提供了一个“静态”接口.Laravel 内置了很多门面,你可能在不知道的情况下正在使用它们.Laravel 的门面作为服务容器中底层类的“静态代理”,相比于传统静态方法,在 ...

  2. 转: Laravel 自定义公共函数的引入

    来源:Laravel 自定义公共函数的引入 背景习惯了 使用 ThinkPHP 框架,有一个公共方法类在代码编写上会快捷很多,所以有必要在此进行配置一番.测试框架:Laravel 5.5步骤指导1. ...

  3. laravel 自定义验证 Validator::extend

    laravel 自定义验证 $messages = [ 'name.integer' => '名字不能为整型', 'name.max' => '长度不能超过5', ]; public st ...

  4. laravel 自定义常量方法

    laravel 自定义常量方法 版本5以上验证OK 常量定义方案A: step1 按路径生成constants文件app/config/constants.php step2 constants文件里 ...

  5. Laravel开发:Laravel框架门面Facade源码分析

    前言 这篇文章我们开始讲 laravel 框架中的门面 Facade,什么是门面呢?官方文档: Facades(读音:/fəˈsäd/ )为应用程序的服务容器中可用的类提供了一个「静态」接口.Lara ...

  6. Laravel自定义Api接口全局异常处理

    在做API时,需要对一些异常进行全局处理,比如添加用户执行失败时,需要返回错误信息 // 添加用户 www.bcty365.com $result = User::add($user); if(emp ...

  7. laravel自定义分页功能的实现:

    laravel版本:5.5.. 执行命令: php artisan vendor:publish --tag=laravel-pagination 在到 resources/views/vendor/ ...

  8. laravel 自定义分页 offset 和 limit 的使用

    laravel 本身有一个自带的快速分页方法 paginate,只需要传入每页显示多少条数据就可以 了,但是如果想使用自定义从哪里开始呢,这时候就可以使用offset 和 limit 的组合,offs ...

  9. laravel php门面模式

    门面模式   理解3个概念: 1)Container的概念,laravel所有的服务都注册在container里面,至于如何注册,就是使用service provider   2)service pr ...

随机推荐

  1. 转载:java中Thread.sleep()函数使用

    点我跳过黑哥的卑鄙广告行为,进入正文. Java多线程系列更新中~ 正式篇: Java多线程(一) 什么是线程 Java多线程(二)关于多线程的CPU密集型和IO密集型这件事 Java多线程(三)如何 ...

  2. 使用Razor Generator构建模块化ASP.NET MVC应用程序

    在构建Web应用程序的时候,我们很难做到模块化的开发,这是因为Web应用程序不仅仅包含编译的C#代码,还包含了js.css和aspx等资源. 在ASP.NET MVC中,我们发布应用程序的时候,还会包 ...

  3. 设计模式のIteratorPattern(迭代器模式)----行为模式

    一.产生背景 迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式.这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示. 二.实现方式 ...

  4. postgresql命令

    连接数据库, 默认的用户和数据库是postgrespsql -U user -d dbname 切换数据库,相当于mysql的use dbname\c dbname列举数据库,相当于mysql的sho ...

  5. 11.redis_python

    # pip install redis import redis # 1.链接数据库 key--value client = redis.StrictRedis(host='127.0.0.1', p ...

  6. 【适合N卡独显电脑的环境配置】Tensorflow教程-Windows 10下安装tensorflow 1.5.0 GPU with Anaconda

    注意: 1.目前Anaconda 更新原命令activate tensorflow 改为 conda activate tensorflow 2. 目前windows with anaconda 可以 ...

  7. [tool] Visual Studio Code python配置

    语言设置 安装中文插件即可成为中文 选择一个Python解释器 Python是一种解释型语言,为了运行Python代码并获取Python IntelliSense,您必须告诉VS Code使用哪个解释 ...

  8. python入门学习:5.字典

    python入门学习:5.字典 关键点:字典 5.1 使用字典5.2 遍历字典5.3 嵌套 5.1 使用字典   在python中字典是一系列键-值对.每个键都和一个值关联,你可以使用键来访问与之相关 ...

  9. web基础之http

    目录 1.Http协议介绍 Http工作原理 http的请求方法 http的响应响应状态码 状态码的类别 常用HTTP状态码简要介绍 用户访问网站携带的参数,以及服务端返回的参数 (http请求报文 ...

  10. 初学Python——文件操作第二篇

    前言:为什么需要第二篇文件操作?因为第一篇的知识根本不足以支撑基本的需求.下面来一一分析. 一.Python文件操作的特点 首先来类比一下,作为高级编程语言的始祖,C语言如何对文件进行操作? 字符(串 ...