laravel-----容器小记

文章来源:http://laravelacademy.org/post/769.html#ipt_kb_toc_769_6

 

例子:超人拥有超能力

控制反转定义:

由外部负责其依赖需求的行为,我们可以称其为 “控制反转(IoC)

class Superman
{
    protected $power;

    public function __construct()
    {
        $this->power = new Fight(9, 100);
        // $this->power = new Force(45);
        // $this->power = new Shot(99, 50, 2);
        /*
        $this->power = array(
            new Force(45),
            new Shot(99, 50, 2)
        );
        */
    }
}

  

 

问题:

耦合度高,“超人” 对 “超能力” 产生依赖。

解决:

不应该手动在 “超人” 类中固化了他的 “超能力” 初始化的行为,而转由外部负责,由外部创造超能力模组

 

实现控制反转的方法------工厂模式,依赖转移

class Superman
{
    protected $power;

    public function __construct(array $modules)
    {
        // 初始化工厂
        $factory = new SuperModuleFactory;

        // 通过工厂提供的方法制造需要的模块
        foreach ($modules as $moduleName => $moduleOptions) {
            $this->power[] = $factory->makeModule($moduleName, $moduleOptions);
        }
    }
}

问题:

耦合度还是高,由原来对多个外部的依赖变成了对一个 “工厂” 的依赖,由于工厂模式下,所有的模组都已经在工厂类中安排好了,如果有新的、高级的模组加入,我们必须修改工厂类

IoC 容器的重要组成 —— 依赖注入

定下了一个接口 (超能力模组的规范、契约),所有被创造的模组必须遵守该规范,才能被生产

interface SuperModuleInterface
{
    /**
     * 超能力激活方法
     *
     * 任何一个超能力都得有该方法,并拥有一个参数
     *@param array $target 针对目标,可以是一个或多个,自己或他人
     */
    public function activate(array $target);
}

定义具体类超能力实现接口

class XPower implements SuperModuleInterface
{
    public function activate(array $target)
    {
        // 这只是个例子。。具体自行脑补
    }
}

超人类依赖接口

class Superman
{
    protected $module;

    public function __construct(SuperModuleInterface $module)
    {
        $this->module = $module;
    }
}

什么叫做依赖注入?

只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于依赖注入(DI)

 

// 超能力模组
$superModule = new XPower;
// 初始化一个超人,并注入一个超能力模组依赖
$superMan = new Superman($superModule);

  

更为先进的工厂 —— 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);
    }
}

 使用方法

// 创建一个容器(后面称作超级工厂)
$container = new Container;

// 向该 超级工厂添加超人的生产脚本
$container->bind('superman', function($container, $moduleName) {
    return new Superman($container->make($moduleName));
});

// 向该 超级工厂添加超能力模组的生产脚本
$container->bind('xpower', function($container) {
    return new XPower;
});

// 开始启动生产
$superman_1 = $container->make('superman', 'xpower');
$superman_2 = $container->make('superman', 'ultrabomb');
$superman_3 = $container->make('superman', 'xpower');

过程:

通过最初的 绑定(bind) 操作,我们向 超级工厂 注册了一些生产脚本,这些生产脚本在生产指令下达之时便会执行。

彻底的解除了 超人 与 超能力模组 的依赖关系,更重要的是,容器类也丝毫没有和他们产生任何依赖,通过注册、绑定的方式向容器中添加一段可以被执行的回调(可以是匿名函数、非匿名函数、类的方法)作为生产一个类的实例的 脚本 ,只有在真正的 生产(make) 操作被调用执行时,才会触发

 

现在的例子中,还是需要手动提供超人所需要的模组参数。

而IoC 容器通过反射机制实现自动在注册、绑定的一堆实例中搜寻符合的依赖需求,并自动注入到构造函数参数中去

具体代码见Illuminate\Container\Container.php中的build方法

public function build($concrete)
    {
        // If the concrete type is actually a Closure, we will just execute it and
        // hand back the results of the functions, which allows functions to be
        // used as resolvers for more fine-tuned resolution of these objects.
        if ($concrete instanceof Closure) {
            return $concrete($this, $this->getLastParameterOverride());
        }

        $reflector = new ReflectionClass($concrete);

        // If the type is not instantiable, the developer is attempting to resolve
        // an abstract type such as an Interface of Abstract Class and there is
        // no binding registered for the abstractions so we need to bail out.
        if (! $reflector->isInstantiable()) {
            return $this->notInstantiable($concrete);
        }

        $this->buildStack[] = $concrete;

        $constructor = $reflector->getConstructor();

        // If there are no constructors, that means there are no dependencies then
        // we can just resolve the instances of the objects right away, without
        // resolving any other types or dependencies out of these containers.
        if (is_null($constructor)) {
            array_pop($this->buildStack);

            return new $concrete;
        }

        $dependencies = $constructor->getParameters();

        // Once we have all the constructor's parameters we can create each of the
        // dependency instances and then use the reflection instances to make a
        // new instance of this class, injecting the created dependencies in.
        $instances = $this->resolveDependencies(
            $dependencies
        );

        array_pop($this->buildStack);

        return $reflector->newInstanceArgs($instances);
    }

重新审视 Laravel 的核心

Laravel 的核心本身十分轻量,并没有什么很神奇很实质性的应用功能。很多人用到的各种功能模块比如 Route(路由)、Eloquent ORM(数据库 ORM 组件)、Request(请求)以及 Response(响应)等等等等,实际上都是与核心无关的类模块提供的,这些类从注册到实例化,最终被你所使用,其实都是 Laravel 的服务容器负责的

服务提供者

我们需要某个服务,就得先注册、绑定这个服务到容器提供服务并绑定服务至容器的东西,就是服务提供者。

为了保证初始化阶段不会出现所需要的模块和组件没有注册的情况,Laravel 将注册和初始化行为进行拆分,注册的时候就只能注册,初始化的时候就是初始化。拆分后的产物就是现在的服务提供者。

服务提供者主要分为两个部分,register(注册) 和 boot(引导、初始化),具体参考文档register 负责进行向容器注册“脚本”,但要注意注册部分不要有对未知事物的依赖,如果有,就要移步至 boot 部分

门面

Router::func()  中的router类 ,实际上是 Illuminate\Support\Facades\Route 通过 class_alias() 函数创造的别名而已。

<?php 
    namespace Illuminate\Support\Facades;

    /**
     * @see \Illuminate\Routing\Router
     */
    class Route extends Facade {

        /**
         * Get the registered name of the component.
         *
         * @return string
         */
        protected static function getFacadeAccessor()
        {
            return 'router';
        }

}





//Face基类的__callStatic方法
public static function __callStatic($method, $args)
{
//获取router实例
$instance = static::getFacadeRoot();

if (! $instance) {
throw new RuntimeException('A facade root has not been set.');
}

return $instance->$method(...$args);
}

//获取facade背后的根对象
public static function getFacadeRoot()
{
//从容器中解析facade根实例
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
 

其中继承的基类Face中有一个__callStatic方法,而getFacadeAccessor()返回一个 route,事实上,这个值被一个ServiceProvider(Illuminate\Foundation\Support\Providers\RouterServiceProvider.php)注册过,

这个才是真正的路由类(Illuminate\Routing\Router.php)!


//真正的Router类

use Illuminate\Routing\Router;

//RouteServiceProvider
public function __call($method, $parameters)
{
    return call_user_func_array(
        [$this->app->make(Router::class), $method], $parameters
    );
}

 

posted @ 2018-03-20 14:49  rcj_飞翔  阅读(92)  评论(0)    收藏  举报