通过IoC容器的正确方法

sit*_*lge 14 php oop dependency-injection inversion-of-control

我身边让我的头DIIoC; 现在使用疙瘩.假设我在执行流程的早期定义了IoC

$container = new Injection\Container();

$container['config'] = function ($c) {
    return new Config($c['loader']);
};

$container['request'] = function ($c) {
    return new Request($c['config']);
};

...
Run Code Online (Sandbox Code Playgroud)

和一个路由器类 call_user_func_array

//$class = 'Dog', $method = 'woof', $this->args = ['foo', 'bar']
call_user_func_array(array(new $class, $method), $this->args);
Run Code Online (Sandbox Code Playgroud)

所以新对象在没有意识到的情况下被实例化,IoC但我仍然希望重用一些定义的服务.

class Dog
{
    public function woof($var1, $var2)
    {
        //$request = IoC service here
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是:

  1. 什么是传递IoC到类的正确方法(静态似乎是邪恶的...)或
  2. 是否有必要传递容器并存在其他方法/概念?

阅读一些不错的文章,但无法弄清楚

更新

我这样做的邪恶方式是定义另一个保存IoC静态属性的服务

$container['services'] = function ($c) {
    return Services::create($c); //make the service
};

$container['services']; //call the service
Run Code Online (Sandbox Code Playgroud)

并在以后访问它

class Dog
{
    public function woof($var1, $var2)
    {
        $services = new Services();

        $request = $services['request']; //retrieving the request service
    }
}
Run Code Online (Sandbox Code Playgroud)

更新2

决定使用最不危害的方式

//passing the container here
call_user_func_array(array(new $class($container), $method), $this->args);
Run Code Online (Sandbox Code Playgroud)

并存储参数 __constructor

public $container;

public function __construct(Injection\Container $container)
{
    $this->container = $container;
}
Run Code Online (Sandbox Code Playgroud)

Nig*_*888 11

IoC通常使用2种模式,两者都是支持者.

  1. 依赖注入
  2. 服务定位器

然而,似乎越来越多的DI赢得了服务定位器模式.许多DI容器使得使用Service Locator变得更加困难,并且在文档中发出警告,不要走这条路.

在DI中,除非该类是组合根的一部分,否则永远不会将容器传递给类.组合根通过在应用程序的入口点处解析对象图来启动所有运动,并且从那里应用程序完全不知道DI容器(没有引用它).请注意,此对象图可能包含创建类的运行时实例的抽象工厂(通过注入要从DI容器解析的函数或仅通过新建它们).

服务定位器是频谱的另一端.通常,容器要么是静态的,要么作为唯一依赖项传递给类.以这种方式构建类可能更容易,但是当您实际需要配置 DI容器时,您需要付出代价.

使用DI,类的依赖关系是显式的,因此您无需进一步查看构造函数参数.使用Service Locator,配置依赖项要复杂得多.这是主要原因(实际上有很多原因)为什么近年来它被认为是一种反模式.

因此,要回答您的问题,如果您想要遵循IoC的现代方法,请不要将IoC容器传递到应用程序中.而是在应用程序的入口点使用组合根来配置容器并构建对象图.

依赖注入示例

这里可以看到PHP中的完整示例.我在关于Pimple项目的讨论中找到了这个页面.

所以,举个例子:

class Dog
{
    public function woof($var1, $var2)
    {
        //$request = IoC service here
    }
}
Run Code Online (Sandbox Code Playgroud)

您需要添加一个构造函数来接受您的$ request服务,以便您的类将接收它的实例.

class Dog
{
    protected $request;

    public function __construct(Request $request)
    {
        $this->request = $request;
    }

    public function woof($var1, $var2)
    {
        //Use $request here, disregard the IoC container
        $this->request->doSomething()
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您将有一个部分,您可以在组合根中定义控制器.这是注入依赖项的地方.

$container = new Injection\Container();

$container['config'] = function ($c) {
    return new Config($c['loader']);
};

$container['request'] = function ($c) {
    return new Request($c['config']);
};

$container['dog'] = $container->factory(function ($c) {
    return new Dog($c['request']);
});

$container['user_controller'] = $container->share(function ($container) {
    return new UserController(
        $container['dog'] //,
        // $container['someOtherDependency']
    );
});
Run Code Online (Sandbox Code Playgroud)

如您所见,使用此方法,您的Dog类完全不知道DI容器.