控制反转(DI) 依赖注入(IoC)

<?php
Class Fight{
    public function __construct($options){
        //...
    }
}

Class Force{
    public function __construct($options){
        //...
    }
}


Class Shot{
    public function __construct($options){
        //...
    }
}

//超能力工厂类
Class SuperModuleFactory{
    public function makeModule($moduleName, $options){
        switch($moduleName){
            case 'Fight':
                return new Fight($options[0], $options[1]);
            case 'Force':
                return new Force($options[0]);
            case 'Shot':
                return new Shot($options[0], $options[1], $options[2]);
            // case 'more': .......
            // case 'and more': .......
            // case 'and more': .......
            // case 'oh no! its too many!': .......
        }
    }
}

Class Superman{
    protected $power;

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

        //通过工厂提供的方法制造需要的模块
//        $this->power = $factory->makeModule('Fight',[9,100]);
//        $this->power = $factory->makeModule('Force',[50]);
//        $this->power = $factory->makeModule('Shot',[99,150,20]);

        /*
        $this->power = array(
            $factory->makeModule('Force',[45]),
            $factory->makeModule('Shot',[99,40,10])
        );
        */

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

    }
}

//创建超人
$superman = new Superman([
    'Fight' => [9, 100],
    'Shot' => [99, 50, 2]
]);
//“超人”的创建不再依赖任何一个“超能力”的类,如果修改了或者增加了新的超能力只需要针对修改SuperModuleFactory即可。




?>

<?php
//当超能力急需拓展的时候,如果依赖超能力工厂就会在switch里堆很多东西,我们需要制定统一接口作为一种“契约”,这样无论是谁创建出的模组,都符合这样的接口,就可以被正常使用。
interface SuperModuleInterface{
    /**
     * 超能力激活方法
     *
     * 任何一个超能力都得有该方法,并拥有一个参数
     *@param array $target 针对目标,可以是一个或多个,自己或他人
     */
    public function activate(array $target);
}

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

/**
 * 终极炸弹 (就这么俗)
 */
class UltraBomb implements SuperModuleInterface{
    public function activate(array $target){
        // 这只是个例子。。具体自行脑补
    }
}

//提供的模组实例必须是一个SuperModuleInterface接口的实现
//正是由于超人的创造变得容易,一个超人也就不需要太多的超能力,我们可以创造多个超人,并分别注入需要的超能力模组即可。
//开头到现在提到的一系列依赖,只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于依赖注入(DI)
class Superman{
    protected $module;

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

//下面就是一个典型的依赖注入

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




//我们需要自动化 —— 最多一条指令,千军万马来相见
//工厂模式升华 -- IoC容器
class Container{
    protected $binds;
    protected $instance;

    public function bind($abstract, $concrete){
        if($concrete instanceof Closure){
            //instanceof Closure判断$concrete是否是一个闭包
            $this->binds[$abstract] = $concrete;
        }else{
            // instance 方法绑定一个已存在的对象实例到容器,这里不是绑定只是把concrete放到数组里
            $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('xpower', function($container) {
    return new XPower;
});

//同上
$container->bind('ultrabomb', function($container){
    return new UltraBomb;
});

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

//绑定后的XPower以及UltraBomb等超能力 使用$container->make()就可以返回实例
//$container->make('superman','xpower')可以实例化superman注入xpower的实例
//因为superman bind的时候闭包实现了对第二个参数(moduleName)的实例化
//所以就可以直接通过超级工厂类穿件superman的实例
//*********************** 华丽丽的分割线 *************************
//开始启动生产
$superman_1 = $container->make('superman','xpower');
$superman_1 = $container->make('superman','ultrabomb');
$superman_1 = $container->make('superman','xpower');
// ...随意添加


//要添加特异功能的参数我觉得应该修改代码如下,当然也需要修改一下前面的XPower类
$container->bind('xpower',function($container, $parameters){
    return new XPower($parameters);
    //然后XPower的构造函数接收参数做相应处理
});

$container->bind('superman', function($container, $moduleName, $parameters){
    return new Superman($container->make($moduleName, $parameters));
});

$superman_x = $container->make('superman','xpower',array(9,50,25));
//代码没有运行过不知道这样可不可行 只是一个思路


//实际上,真正的 IoC 容器更为高级。
//我们现在的例子中,还是需要手动提供超人所需要的模组参数,但真正的 IoC 容器会根据类的依赖需求,自动在注册、绑定的一堆实例中搜寻符合的依赖需求,并自动注入到构造函数参数中去。
//Laravel 框架的服务容器正是这么做的。实现这种功能其实理论上并不麻烦,但我并不会在本文中写出,因为……我懒得写。
?>

http://laravelacademy.org/post/769.html