服务容器 IOC,DI 分析

DI

DI ( Dependency Injection,简称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

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。
通过 DI 我们可以看到,一个类所需要的依赖类是由我们主动实例化后传入类中的。
控制反转和这个有什么关系呢?

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

	namespace App\Http\Controllers;

	use Illuminate\Http\Request;

	class SessionController extends Controller
	{

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

以上是一个 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();
				}

		}
}

这段代码的问题在于如果我们又要增加一钟键盘,那我们又得对这个类进行修改。这样下去,这个类会变得庞大且耦合程度过高。我们可以用工厂模式来改造它。

// 简单工厂
class Factory
{
    private static $context = null;

    public static function getInstance($type)
    {
        switch ($type) {
            case 'common':
                self::$context = new CommonKeyboard();
                break;
            case 'awesome':
                self::$context = new AweSomeKeyboard();
                break;
            default:
                self::$context = new Keyboard();
                break;
        }
        return self::$context;
    }
}

class Computer
{
    protected $keyboard;

    /**
     * Computer constructor.
     * @param $keyboard
     */
    public function __construct($type = null)
    {
        $this->keyboard = Factory::getInstance($type);
    }
}

这样使用简单工厂模式后,我们后续的修改可以不用对Computer类进行操作而只要修改工厂类就行了。这就相当于对 Computer 类进行了解耦。
Computer 类虽不在依赖那些键盘类了,但是却变为依赖工厂类了。
后续添加新类型的键盘就必须对工厂类进行修改。
所以这个工厂类还不能很好的满足要求,我们知道电脑对键盘的接口都是一致的,键盘必须实现这一接口才能被电脑识别,那我们对 Computer 和Keyboard 类进行修改。

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 MechanicalKeyboard());

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

<?php
/**
 * Created by PhpStorm.
 * User: v_bivwei
 * Date: 2019/10/15
 * Time: 15:04
 */


class Container
{
    protected $binds = array();
    protected $instances = array();

    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);
    }

}


class CommonBoard
{
    public function myType()
    {
        var_dump("我是一个普通键盘");
    }
}

class MechanicalBoard
{
    public function myType()
    {
        var_dump("我是一个 MechanicalBoard 键盘");
    }
}

class Computer
{
    public $board = null;

    /**
     * Computer constructor.
     * @param null $board
     */
    public function __construct($board)
    {
        $this->board = $board;
    }


    public function cc()
    {
        var_dump("我是电脑类");
    }
}

$container = new Container();
$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']); // 绑定的方法执行
$computer->cc();
$computer->board->myType();

结果:
file

现在只要对 bind 绑定的 Board 类的实现进行修改,我们就可以很容易替换掉键盘了:

class MechanicalBoard
{
    public function myType()
    {
        var_dump("我是一个 MechanicalBoard 键盘");
    }
}
$container->bind('Board', function ($container) {
    return new MechanicalBoard();
});

结果:
file
以上就是一个简单的服务容器。

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

参考文章 :

http://www.txwei.cn/2019/08/02/laravel_ioc/

发布了145 篇原创文章 · 获赞 24 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/xiaobinqt/article/details/102573018