在自己的可重用库中提供依赖注入

时间:2017-07-22 11:04:00

标签: php dependency-injection composer-php

背景

我正在编写一些可重用的库,其中包含很少的类。 其中一个需要依赖,因为一些更复杂的逻辑,我想将该类的责任委托给其他地方(另一个类)。

目标

我不想创建一个包,例如Symfony Bundle可以处理我的依赖注入,并提供一种将其与客户端代码集成的简单方法。 我的目标是提供可重用且独立于框架的解决方案。

其他信息

我正在使用composer,而且我已经阅读过像php-di这样的DI容器。 php-di有一个demo应用程序示例,但它不符合我的要求。

实施例

库代码片段

<?php

class W3CAnalyzer implements WebStandardAnalyzer
{
   private $httpClient;

   public function __construct(HttpClient $httpClient)
   {
      $this->httpClient = $httpClient;
   }

   public function analyze(string $url): WC3AnalysisMetaData
   {
      $siteContent = $this->httpClient->getContent($url);

      //further logic there
   }
}

正如您所看到的,W3CAnalyzer包含依赖项HttpClient类。 我会以某种方式配置一个DI容器来注册并解决该依赖关系。

示例客户端代码

<?php

class WebStandardController
{
    private $webStandardAnalyzer;

    public function __construct(WebStandardAnalyzer $webStandardAnalyzer)
    {
       $this->webStandardAnalyzer = $webStandardAnalyzer;
    }
}    

在客户端项目中,程序员将使用他的DI配置注册W3CAnalyzer作为WebStandardAnalyzer注入控制器(它并不重要)。

问题

如果我在内部使用我的库中的一些DI配置和容器,我如何将此配置与客户端代码集成,那么是否有可能注册库类并且其所有依赖关系都已解决了? 我还不知道,我将如何在我的库中组织DI(这实际上是问题的第一部分),我想以某种方式使用上面提到的php-di库。

总结一下,如何在非框架可重用库中组织DI,以便可以在每个其他项目中使用该库(无论使用哪个框架),并且其(该库)依赖项也将在运行时期间解析项目

谢谢!

1 个答案:

答案 0 :(得分:1)

class W3CAnalyzerFactory
{
    public function build()
    {
         $Subject = new W3CAnalyzer(new HttpClient);

         return $Subject
    }
}

以上是我如何利用工厂模式,并取得了巨大的成功。如果new HttpClient不足以满足您的代码(也就是说,它也需要构建),请执行以下操作:

class HttpClientFactory
{
    public function build()
    {
         //modify this to actually construct the HttpClient object, with
         //constructor and/or setter injections
         $Subject = new HttpClient();

         return $Subject
    }
}

然后,您的W3CAnalyzerFactory将如下所示:

class W3CAnalyzerFactory
{
    public function build()
    {
         $Subject = new W3CAnalyzer(
             (new HttpClientFactory)
             ->build()
         );

         return $Subject
    }
}

然后,使用您的图书馆的人可以执行以下操作:

$WebStandardController = new WebStandardController(
    (new W3CAnalyzerFactory())
    ->build()
);

或者,他们想要使用自己的DI设置。正如你所说,注入它并不重要,只是他们可以使用W3CAnalyzerFactory类,然后一切就绪了。

提示:对于我的库和/或应用程序中的所有对象,我总是创建一个工厂类,即使没有注入任何内容。起初感觉罗嗦,但有一些好处。

  1. 如果你曾经重构一个类(如HttpClient)以便现在需要注入一些东西,那么使用HttpClientFactory的所有内容都已经完成,你不需要去通过new HttpClient()替换所有(new HttpClientFactory)->build()行。
  2. 您不必记住哪些注入的实施需要工厂,哪些不需要建造这些工厂。一切都做,所以只需使用工厂。 (即我是否使用new HttpClient(new HttpClientFactory)->build()?请不要考虑它,只需使用工厂。)
  3. 在我创建实现时创建这些工厂是我推荐的,因为它让它不受影响,并且不要求你稍后再回来并试着想出如何将事物串在一起。

    我认为这种方法对于依赖注入来说是最简单的,当你不想或不能使用容器来帮助你时。

    旁注:我将对象分配给$Subject,然后return $Subject分隔一行而不是仅仅执行return new Whatever...的原因是因为我倾向于选择setter注入,这意味着我需要在构造之后对对象做事。尽管如此,您可以做最适合您的库和/或应用程序的方法。选择单词&#34; subject&#34;因为变量名称来自于我的单元测试$SUT中调用我的对象的&#34;主题测试&#34;。这些都没有经过测试,所以我只是称它们$Subject有一个很好的一致名称,可以帮助我轻松查看正在构建的对象。我的习惯再次完全是可选的。