将Boy对Girl的依赖转移到了类定义之外,这样Boy不在主动的依赖Girl,而是需要在初始化的时候将Girl作为一个参数传入构造函数。像这种依赖不是主动的初始化解决,而是由外部传入依赖解决的方式,我们就可以理解为依赖注入
。这种将依赖的控制权转交出去的行为便是控制反转
通过接口(interface)实现依赖注入解耦:
interface Girl {
public function cook();
}
class LolitaGirl implement Girl {
public function cook() {
echo "萝莉做的饭还需努力";
}
}
class LadyGirl implement Girl {
public function cook() {
echo "女士做的饭很好吃";
}
}
class Boy {
// 我需要个妹子做饭给我吃
protected $girl;
public function __construct(Girl $girl) {
$this->girl = $girl;
}
}
$lolitaGirl = new LolitaGirl();
$ladyGirl = new LadyGirl();
// 我要吃萝莉的饭
$boy = new Boy($lolitaGirl);
// 我要吃女士的饭
$boy = new Boy($ladyGirl);
使用魔术方法__set(),__get()动态添加依赖
class Boy {
protected $module;
public __construct() {
$this->module = [];
}
public function __set($name, $value) {
$this->module[$name] = $value;
}
public function __get($name) {
if (array_key_exists($name, $this->module)) {
return $this->module[$name];
}else {
throw new \Exception($name . 'is not exists');
}
}
}
$boy = new Boy();
$boy->ladyGirl = new LadyGirl();
$boy->skill = new Skill(10000);
抽象工厂模式:
Boy所依赖的属性越来越多,会出现很多setter。你会发现这些setter的行为都一样,完全可以抽象出一个工厂帮你去set
class FactoryBoy {
public function createModule($module, $option = []) {
if (class_exists($module)) {
if ([] == $option) {
return new $module();
} else {
return new $module($option);
}
}else {
throw new \Exception('class ' . $module . ' is not exists!');
}
}
}
class Boy {
public function __construct($modules) {
$factory = new FactoryBoy();
foreach($modules as $moduleName => $moduleOption) {
$this->data[] = $factory->createModule($moduleName, $moduleOption);
}
}
}
$boy = new Boy([
LadyGirl::class => '',
Skill::class => 10000,
]);
依赖注入容器(dependency injection container):
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 = []) {
if (isset($this->instances[$abstract])) {
return $this->instances[$abstract];
}
array_unshift($parameters, $this);//往$parameters数组添加元素$this
return call_user_func_array($this->binds[$abstract], $parameters);
}
}
$container = new Container();
$container->bind('Boy', function($container, $moduleName) {
return new Boy($container->make($moduleName));
});
$container->bind('LadyGirl', function($container) {
return new LadyGirl();
})
// 一个拥有了ladyGirl做饭的Boy
$boy = $container->make('Boy', ['LadyGirl']);
call_user_func_array函数使用说明
function foobar($arg, $arg2) {
echo __FUNCTION__, " got $arg and $arg2\n";
}
class foo {
function bar($arg, $arg2) {
echo __METHOD__, " got $arg and $arg2\n";
}
}
// Call the foobar() function with 2 arguments
call_user_func_array("foobar", array("one", "two"));
// Call the $foo->bar() method with 2 arguments
$foo = new foo;
call_user_func_array(array($foo, "bar"), array("three", "four"));
依赖注入与容器 https://www.cnblogs.com/tingyugetc/p/6234560.html
call_user_func_array函数说明 https://www.php.net/manual/zh/function.call-user-func-array.php
Laravel 服务容器实例教程--深入理解控制反转(IoC)和依赖注入(DI)https://laravelacademy.org/post/769.html