Laravel 架构中的 Container/ServiceProvider/Facade

最近有空继续学习laravel,对laravel文档中系统架构部分基本的有一些了解,和大家分享一下。

container容器

先来说一下这货是干什么的。现在假设一下你想要做一个开源项目,写一个类实现某种功能。OK,那么再假设,你这个类要做的工作比较复杂,需要

  • 操作数据库
  • 缓存
  • 操作静态文件
  • 操作session

等等……想想都复杂,不过没有关系,以上几个部分显而易见地看出来,它们之间的似乎没什么关联。那么你可以考虑把以上几部分写成独立的类,也就是说针对数据库操作我们写一个DB类封装一些常用操作,针对缓存我们写一个Cache类……
那么好了,当要使用这个类时,可能就会看到这样的代码

<?php
  class SomeClass {

    public function dbTask() {
      $db = new DBClass(['localhost',3306,root,pwd]);
      //数据库操作
    }

    public function cacheTask() {
      $cache = new Cache(['localhost',11211]);
      //缓存操作
    }
    //...
  }

看起来其实还好,但是想想,如果SomeClass依赖很多外部的类,我们每次使用SomeClass都必须先use进那个类,再通过参数实例化那个对象,这样非常麻烦。那么,这时候就产生了容器的概念。
什么叫容器呢?简单讲,就是把我们的应用可能用到的service(像上述那些类,因为专职某个功能,称它们为service),全部绑定到一个全局的对象里面,这个对象就叫容器。可能就像下面这样

class Container {

  protected $service_arr = [];

  public function bind($name, $instance) {

     $this->servie_arr[$name] = $instance;

  }

  public function get($name) {

    return $this->service_arr[$name];

  }

}

那么我们以后要用到某些service的时候,就可以直接通过Container->get('some')的方式来获取实例了比如

<?php
  class SomeClass {

    priavte $container;

    public function construct($con) {

       $this->container = $con;

    }

    public function dbTask() {

      $db = $this->container->get('db');
      //数据库操作
    }

    public function cacheTask() {

      $cache = $this->container->get('cache');
      //缓存操作
    }
    //...
  }

OK,现在你大概明白container到底是干嘛的了吧。再简单一点讲,就是把我们可能需要在代码内部手动实例化的对象,全部绑定到container这个对象上,以后要用就来这儿取。至于它的好处我就不赘述了,可以看看设计模式关于IoC和DI的章节(控制反转和依赖注入)

ServiceProvider

理解了上面的container,你就会有疑问

如果要把所有可能的service绑定,container类是否会变得非常庞大?

是的,如果你在应用的开始调用N次bind方法,依次绑定需要的所有service,这个文件将难以维护,比如

class Container {

  protected $service_arr = [];

  public function bind($name, $instance) {

     $this->servie_arr[$name] = $instance;

  }

  public function get($name) {

    return $this->service_arr[$name];

  }

}

$con = new Container();

$db = new DBClass(['locahost',3306,root,pwd]);

$con->bind('db', $db);

$cache = new Cache(['127.0.0.1',11211]);

$con->bind('cache',$cache);

//...

所有的类的实例化都写到一个文件里了,讲会是耦合性升高。
ServiceProvider就是解决这个问题的
每一个需要绑定到container的service,你需要创建一个对应ServiceProvider,这个ServiceProvider中有一个register方法,在这里面进行绑定,而不是像上述那样。比如:

class SomeServiceProvider extends ServiceProvider {

  public function register() {

      $this->app->bind('some', new Some());

  }

}

Laravel的container为了知道需要绑定哪些service,它会去读一个数组,这个数组是config/app.php中的Providers,然后你只需要把你的SomeServiceProvider写进这个数组,就可以绑定上了。

Facade

刚开始看Facade其实还是不太好理解,到底什么是Facade呢,它存在的意义又是什么呢?
我的理解

  • 简化对service的使用,可以理解为语法糖
  • 可以方便的替换

可能你有疑惑,到底替换什么呢?看看下面的代码吧

use Illuminate\Support\Facades\Redis;

class IndexController extends Controller {

  public function index() {

    $redis = Redis::connect();

    //do something

  }

}

然而当我们打开Illuminate\Support\Facades\Redis看看

file

并没有connect的静态方法啊?但是可以发现唯一的一个方法返回了一个redis字符串,有什么玄机呢
那么我们再看看RedisServiceProvider

file
我们的RedisServiceProvider在container里绑定了一个redis……所以大概你也猜到了,没错!

Redis::connect() 等价于 $this->app->get('redis')->connect()

get的到底是什么,取决于Facade中getFacadeAccessor方法返回的字符串!
这样有什么好处呢?文档也说了,除了简便使用以外最大的用处是测试,想想看,你把Redis的getFacadeAccessor方法返回值变成'memcached',那么你所有使用Redis::some()是不是就全部切换成memcache啦?

本帖已被设为精华帖!
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 12
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!