依赖注入(DI)与服务容器(IoC)

参考文章:http://www.yuansir-web.com/2014/03/20/%E7%90%86%E8%A7%A3php-%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5laravel-ioc%E5%AE%B9%E5%99%A8/?preview

我们在建一个类时,在类的内部有可能会用到另外一个对象实例举个栗子:

class User{
    public function getUser(){
        $db = new DB(); // 这里产生了对数据库连接类的依赖
        $db->select('select username form user..');
        ...
    }
}

当我们修改了DB这个类时肯定要回来修改User这个类,可以想象DB类肯定不是只用在User类中,一个项目中要有许多类要用到DB类,如果DB类修改了(举个不恰当的例子:DB这个类改名了)那必须要把项目中所有用到db类的类都要作修改。

这个时候可以这样解决

class User{
    public function getUser(DB $db){
        //$db = new DB(); // 依赖消失
        $db->select('select username form user..');
        ...
    }
}

这个时候想调用getUser方法就必须要“注入”$db。

实际中我们的User类肯定不是只会用到getUser这个方法的,还有可能会用到getUsername、getUserAge、getUserSex...等等,难道我们要为每个方法都加一个$db参数吗?当然不可能所以我们再来完善一下这个User类:

class User{
    private $db;
    //*
    public function __construct(DB $db){
        $this->db = $db;
    }
    /*/
    public function getDb(DB $db){
        $this->db = $db;
    }
    //*/
    public function getUser(){
        $this->db->select('select username form user..');
        ...
    }
    public function getUserAge(){
        $this->db->select('select userage form user..');
        ...
    }
    public function getUserSex(){
        $this->db->select('select usersex form user..');
        ...
    }
}


可以看到我们可以在构造器和getDb方法中注入$db。

好了,我们已经实现“依赖反转”了!但是!!!假如我们User类还需要其他类呢?举个很不恰当的牵强的栗子:

我们把username放到memcache 中,把userage放到session中,把usersex放到redis中(这。。。很不实际)那我们的类就变成这个样子了

class User{
    private $db;
    private $mem;
    private $redis;
  ...
public function __construct(DB $db , Mem $mem , Redis $redis , Session $session ...... ){ $this->db = $db; $this->mem = $mem; $this->redis = $redis; } public function getUser(){ $this->db->select('select username form user..'); ... } public function getUserAge(){ $this->mem->get('userage'); ... } public function getUserSex(){ $this->redis->get('usersex'); ... } }

要在控制器里放那么多实例。而且如果要使用User类的话还有实例这么多存贮类,不累吗?代码肯定一团糟!

这个时候可以用一个工厂来“完善”一下:

class UserFactory{
    public static function getUserObj(){
        $db = new DB();
        $mem = new Mem();
        ...
        $user = new ($db , $mem , ...);
        return $user;
    }
}

这样就好看多了,但是!!!然并卵,我们还是要实例化各个存储类(db,mem,redis。。。)

现在该服务容器(IoC)登场了:

class User{
    private $IoC;
    public function __construct(IoC $IoC){
        $this->IoC = $IoC;
    }
    
    public function getUser(){
        $this->IoC->get('db')->select('select username form user..');
        ...
    }
    public function getUserAge(){
        $this->IoC->get('mem')->get('userage');
        ...
    }
    public function getUserSex(){
        $this->IoC->get('redis')->get('usersex');
        ...
    }
}

$IoC = new IoC();
// 这里使用了闭包函数,$di->set的时候实际上并没有实例化,
// 而是在$di->get()的时候才执行了匿名函数并将对象返回。
$IoC->set('db' , function(){
    return new DB(...);
});
$IoC->set('mem' , funciton(){
    return new Mem(...);
});
$IoC->set('redis' , funciton(){
    return new Redis(...);
});
$user = new User($IoC);

大功告成!!!!!!

DI与IoC就是这么简单。

分享几个链接:

symfony中服务容器:

http://www.cnblogs.com/Seekr/archive/2012/06/19/2554934.html

听 Fabien Potencier 谈Symfony2 之 《What is Dependency Injection ?》

http://www.cnblogs.com/Seekr/archive/2012/06/20/2556463.html

另外还有laravel、struts2、spring有关依赖注入的文章可以自行搜索。

原文地址:https://www.cnblogs.com/orlion/p/4797422.html