在A类中使用了B类的实例时,B对象的构造不是在A类某个方法中初始化的,而是在A类外部初始化之后以B类的对象传入进来。这个过程就是依赖注入。
这在我们PHP里感触不明显,觉得修改$config和修改$travel = new drive(60,1000)差别不到,一方面是便于管理,另一方面是PHP的非编译型的特性优势。如果用Java来实现,感触就非常明显了,比如程序已经打包了,只需要修改配置文件,就可以控制类的依赖关系了。起到了解耦的作用。
只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于依赖注入(DI)
1.原始代码:
class Superman
{
protected $power;
public function __construct()
{
$this->power = new Fight(9, 100); //类嵌套了类 会造成耦合 而且这里面可能会有多个 子类,就会更容易耦合 比如 跑步类、飞行类 by Paul
}
}
2.工厂模式: 工厂模式只能选择一种小类进行拼装 不能灵活随意组装多个插件 by Paul
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]);
}
}
}
//统一建一个工厂类,管理里面的小类,这样只需要换掉工厂类即可减低耦合 by Paul
class Superman
{
protected $power;
public function __construct(array $modules)
{
// 初始化工厂
$factory = new SuperModuleFactory;
// 通过工厂提供的方法制造需要的模块
foreach ($modules as $moduleName => $moduleOptions) {
$this->power[] = $factory->makeModule($moduleName, $moduleOptions);
}
}
}
// 创建超人
$superman = new Superman([
'Fight' => [9, 100],
'Shot' => [99, 50, 2]
]);
3.依赖注入:
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)
{
// 这只是个例子。。具体自行脑补
}
}
class Superman
{
protected $module;
public function __construct(SuperModuleInterface $module)
{
$this->module = $module;
}
}
$superModule = new XPower;
$superMan = new Superman($superModule); //最重要的点是把实例化的类作为参数传入进 类中,需要什么就传入什么类作为参数,这样小类就不会写入到主类里面了,也可以解决第一点的问题,通过工厂模式和依赖注入,可以完成把多个子类封装成一个工厂类,再把工厂类传递给主类 by Paul
4.ioc容器 - 更先进的工厂: 容器实现了随意组装各种超人,而不是一个固定工厂类了,可以装配有超能力的超人,有炸弹能力的超人,或者既有超能力又有炸弹能力的超人 by Paul
class Container
{
protected $binds;
protected $instances;
// 绑定生产对象脚本
public function bind($abstract, $concrete)
{
if ($concrete instanceof Closure) {
// 添加脚本
$this->binds[$abstract] = $concrete;
} else {
// 添加已经写好的对象
$this->instances[$abstract] = $concrete;
}
}
// 执行生产对象
public function make($abstract, $parameters = [])
{
// 直接返回已保存的对应$abstract的对象
if (isset($this->instances[$abstract])) {
return $this->instances[$abstract];
}
// 执行脚本,生产指定$abstract, $parameters的对象
array_unshift($parameters, $this);
return call_user_func_array($this->binds[$abstract], $parameters);
}
}
// 创建一个容器(后面称作超级工厂)
$container = new Container;
//by Paul 重点:把工厂模式里面的工厂类中构建的各种小类组件,做成了可以随意添加的绑定方式,更加灵活,包括主类也绑定到了容器里
// 向该 超级工厂添加超人的组件(主类 by Paul)
$container->bind('superman', function($container, $moduleName) {
return new Superman($container->make($moduleName));
});
// 向该 超级工厂添加超能力组件
$container->bind('xpower', function($container) {
return new XPower();
});
// 向该 超级工厂添加炸弹组件
$container->bind('ultrabomb', function($container) {
return new UltraBomb();
});
// ****************** 华丽丽的分割线 **********************
// 开始启动生产
$superman_1 = $container->make('superman', ['xpower']); //装配有超能力的超人 by Paul
$superman_2 = $container->make('superman', ['ultrabomb']);//装配有炸弹的超人 by Paul
$superman_3 = $container->make('superman', ['xpower','ultrabomb']);装配有超能力、炸弹能力的超人 by Paul
// ...随意添加