Jet*_*rst 5 php oop dependency-injection inversion-of-control
我试图理解依赖注入容器的作用,因为它使我成为可维护代码的基础.
据我了解,DIC正如标题所暗示的那样:一个容器,其中所有依赖项都被收集在一起.new Foo\Bar所有新实例都是在容器内部生成的,而不是遍及整个应用程序,然后在需要它们的地方传递给彼此(例如,Model用实例实例化,实例Database化实例Config).
我试图制作一个非常简单的 DIC.这是结果.
在我的前端控制器中,我正在实例化一个新的App\Core\Container.
我Container看起来像这样:
<?php
namespace App\Core;
use App\Config;
class Container
{
public $config;
public $router;
public $database;
public $model;
public $view;
public $controller;
public function __construct()
{
$this->config = new Config;
$this->router = new Router;
$this->database = new Database($this->config);
}
public function add()
{
// add dependencies from the outside?
}
public function getInstance(/* string */)
{
// return an instance for use somewhere?
}
public function newModel($model)
{
$model = $this->getModelNamespace() . $model;
$this->model = new $model($this->database);
return $this->model;
}
private function getModelNamespace()
{
$namespace = 'App\Models\\';
if (array_key_exists('namespace', $this->params = [])) {
$namespace .= $this->params['namespace'] . '\\';
}
return $namespace;
}
public function newView($params)
{
$this->view = new View($this->model, $params);
return $this->view;
}
public function newController($controller)
{
$controller = $this->getControllerNamespace() . $controller;
$this->controller = new $controller;
return $this->controller;
}
private function getControllerNamespace()
{
$namespace = 'App\Controllers\\';
if (array_key_exists('namespace', $this->params = [])) {
$namespace .= $this->params['namespace'] . '\\';
}
return $namespace;
}
}
Run Code Online (Sandbox Code Playgroud)
问题
注意:前三个标题回答了您的问题,而以下标题回答了预期的问题,并提供了前两个部分中的任何内容.
不,这看起来不像依赖注入容器.依赖注入容器旨在通过确定,创建和注入所有依赖项来减少实例化所需的工作.相反,你所拥有的似乎是工厂和服务定位器的组合.
工厂抽象对象的创建.这基本上就是你Container班级正在做的事情.通过调用指定的方法(即newModel),您的容器负责定位要实例化的确切对象并构造该对象的实例.
我之所以称之为"糟糕"的工厂,是因为它开始看起来像是用于定位服务.服务定位器通过隐藏对象的依赖关系来工作:GenericService对象可能依赖于服务定位器,而不是依赖于对象.给定服务定位器,它可以请求实例GenericService.我看到类似的行为开始在你add()和getInstance()方法中占据一席之地.服务定位器通常被认为是反模式,因为它们抽象依赖性因此使代码无法测试!
这取决于.你可以很容易地用一个类创建一个简单的依赖注入容器.问题在于,简单容器的性质往往会变得更加先进到一个不那么简单的容器中.当您开始改进模式时,您需要考虑不同组件如何一起使用.问问自己:他们是否遵循SOLID原则?如果没有,重构是必要的.
我在上面说过,但是:依赖注入容器意味着通过确定,创建和注入所有依赖项来减少实例化所需的工作.DIC将查看类的所有依赖关系,以及这些依赖关系可能具有的所有依赖关系等等......在这个意义上,容器负责分层实例化所有依赖关系.
Container您提供的类依赖于对预定义类的非常严格的定义.例如,模型层中的类似乎仅依赖于数据库连接.(关于控制器和视图层中的类,可以说类似的陈述).
依赖注入容器将检测依赖项.通常,这通过3种机制中的1种发生:自动装配,注释和定义.PHP-DI文档提供什么所有这三个意味着一个好主意在这里.简而言之,尽管:自动装配通过反映类来检测依赖关系,但注释用于使用类上方的注释来编写依赖关系,并且定义用于硬编码依赖关系.就个人而言,我更喜欢自动装配,因为它干净简洁.
是的你可以.首先考虑注入器应该能够实例化任何对象(服务,视图,控制器等......).它需要查看相关对象并分层实例化所有依赖项(提示:可能通过某种递归方法).
使用自动装配的简单注射器的快速示例如下所示:
<?php
class Injector
{
public function make($className)
{
$dependencies = [];
//Create reflection of the class-to-make's constructor to get dependencies
$classReflection = new ReflectionMethod($className, "__construct");
foreach($classReflection->getParameters() as $parameter) {
$dependencyName = $parameter->getClass()->getName();
//Use the injector to make an instance of the dependency
$dependencies[] = $this->make($dependencyName);
}
$class = new ReflectionClass($className);
//Instantiate the class with all dependencies
return $class->newInstanceArgs($dependencies);
}
}
Run Code Online (Sandbox Code Playgroud)
使用类似下面的内容进行测试,您可以看到注入器如何递归检查并实例化所有依赖项
class A {
protected $b;
public function __construct(B $b) { $this->b = $b; }
public function output(){ $this->b->foo(); }
}
class B {
protected $c;
public function __construct(C $c) { $this->c = $c; }
public function foo() { $this->c->bar(); }
}
class C {
public function __construct() { }
public function bar() { echo "World!"; }
}
$injector = new Injector;
$a = $injector->make("A");
//No need to manually instantiate A's dependency, B, or B's dependency, C
$a->output();
Run Code Online (Sandbox Code Playgroud)
这种基本喷射器有明显的故障.例如,如果两个类彼此依赖(应该检查它),则有机会创建递归灾难.然而,原样,这作为注射器的基本示例.
为了使其更强大并且属于"依赖注入容器"的定义,您需要一种方法来跨多个make()调用共享实例化实例.例如,您可能有另一个方法share().此方法将存储传递给它的实例.每当通过该make()方法构建类并依赖于先前共享的类,而不是实例化新实例时,它将使用已经实例化的类.
对于一个简单而强大的依赖注入容器,我建议Auryn,但无论如何,在使用已有的之前,尝试理解并创建自己的.
| 归档时间: |
|
| 查看次数: |
661 次 |
| 最近记录: |