依赖注入(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有关依赖注入的文章可以自行搜索。