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
看看
并没有connect的静态方法啊?但是可以发现唯一的一个方法返回了一个redis
字符串,有什么玄机呢
那么我们再看看RedisServiceProvider
我们的RedisServiceProvider在container里绑定了一个redis
……所以大概你也猜到了,没错!
Redis::connect()
等价于$this->app->get('redis')->connect()
get的到底是什么,取决于Facade中getFacadeAccessor
方法返回的字符串!
这样有什么好处呢?文档也说了,除了简便使用以外最大的用处是测试,想想看,你把Redis的getFacadeAccessor方法返回值变成'memcached',那么你所有使用Redis::some()
是不是就全部切换成memcache啦?