IOC(inversion of control)控制反转模式;控制反转是将组件间的依赖关系从程序内部提到外部来管理;
DI(dependency injection)依赖注入模式;依赖注入是指将组件的依赖通过外部以参数或其他形式注入;
两个说法本质上是一个意思。
例如:
class DbMysql
{
public function query(){}
}
class Controller
{
public $db;
public function __construct()
{
$this->db = new DbMysql();
}
public function action()
{
$this->db->query();
}
}
$c = new Controller();
$c->action();
Controller类中的action方法需要用到DbMysql类中的query方法,所以Controller类就对DbMysql类产生了依赖,Controller类和DbMysql类之间的耦合度就比较高,因为当DbMysql类的构造函数发生改变的时候,比如由现在的没有参数变成有参数了,参数数量改变了,那么Controller类中的代码都要做出相应改变。
或者说我们现在需要将DbMysql类换成另一个DbOracle类,Controller类中要做出的改变甚至更大。
看下面的另一种写法:
class DbMysql
{
public function query(){}
}
class Controller
{
public $db;
public function __construct($dbMysql)
{
$this->db = $dbMysql;
}
public function action()
{
$this->db->query();
}
}
$db = new DbMysql();
$c = new Controller($db);
$c->action();
Controller类中不需要实例化DbMysql,而是将DbMysql类的实例作为参数传递(或者单独写一个接收实例的方法处理),这样Controller类就完全不用管DbMysql是怎么样实例的,而是仅仅调用DbMysql中的query方法就行了。这种模式就是依赖注入。
第一个例子中Controller类负责实例DbMysql,也就是说Controller类控制着实例DbMysql类的主动权,而第二个例子中将这个主动权提出到Controller类的外面,所以也叫做控制反转。
这样看起来还不错,但是如果我们需要很多类,而且需要自己写的时候弄清楚,每一个类依赖什么类,这样太烦,如果有一个类能够帮我们搞定这个动作那就太爽了。而事实上有这个类,这个类就叫做容器。
下面通过实例与大家分析分析
示例一
class DbMysql
{
public function __construct($host, $name, $pwd)
{
// do something
}
public function query()
{
echo __METHOD__ . PHP_EOL;
}
}
class DbRedis
{
public function __construct($host, $name, $pwd)
{
// do something
}
public function set()
{
echo __METHOD__ . PHP_EOL;
}
}
class controller
{
public $mysql;
public $redis;
public function __construct()
{
$this->mysql = new DbMysql('host', 'name', 'pwd');
$this->redis = new DbRedis('host', 'name', 'pwd');
}
public function action()
{
$this->mysql->query();
$this->redis->set();
}
}
$c = new Controller();
$c->action();
/**
* 输出:
* DbMysql::query
* DbRedis::set
*/
普通的实现方式,耦合度高。
示例二
class DbMysql
{
public function __construct($host, $name, $pwd)
{
// do something
}
public function query()
{
echo __METHOD__ . PHP_EOL;
}
}
class DbRedis
{
public function __construct($host, $name, $pwd)
{
// do something
}
public function set()
{
echo __METHOD__ . PHP_EOL;
}
}
class controller
{
public $mysql;
public $redis;
public function __construct($mysql, $redis)
{
$this->mysql = $mysql;
$this->redis = $redis;
}
public function action()
{
$this->mysql->query();
$this->redis->set();
}
}
$mysql = new DbMysql('host', 'name', 'pwd');
$redis = new DbRedis('host', 'name', 'pwd');
$c = new Controller($mysql, $redis);
$c->action();
/**
* 输出:
* DbMysql::query
* DbRedis::set
*/
实现了依赖注入和控制反转,但是没有使用容器类。
示例三
class DbMysql
{
public function __construct($host, $name, $pwd)
{
// do something
}
public function query()
{
echo __METHOD__ . PHP_EOL;
}
}
class DbRedis
{
public function __construct($host, $name, $pwd)
{
// do something
}
public function set()
{
echo __METHOD__ . PHP_EOL;
}
}
class controller
{
public $mysql;
public $redis;
public function __construct($mysql, $redis)
{
$this->mysql = $mysql;
$this->redis = $redis;
}
public function action()
{
$this->mysql->query();
$this->redis->set();
}
}
class Container
{
public $bindings = [];
public function bind($key, Closure $value)
{
$this->bindings[$key] = $value;
}
public function make($key)
{
$new = $this->bindings[$key];
return $new();
}
}
$app = new Container();
$app->bind('mysql', function () {
return new DbMysql('host', 'name', 'pwd');
});
$app->bind('redis', function () {
return new DbRedis('host', 'name', 'pwd');
});
$app->bind('controller', function () use ($app) {
return new Controller($app->make('mysql'), $app->make('redis'));
});
$controller = $app->make('controller');
$controller->action();
/**
* 输出:
* DbMysql::query
* DbRedis::set
*/
实现了基本的容器类,容器类中有两个方法,bind和make,一个是绑定操作,一个是实例化操作。将每一个需要使用到的类使用关键字绑定到容器类中去,但是每一个类仍然需要手动去实例化,这里引入了闭包函数,主要作用是在调用的时候才真正去实例化,而如果仅仅是绑定了一个类,是不会实例化这个类的。
转载自:zhuanlan.zhihu.com/p/93270244