symfony - 在 Symfony2 中,为什么注入(inject)服务容器而不是单个服务是一个坏主意?

标签 symfony dependency-injection

我找不到这个问题的答案...

如果我注入(inject)服务容器,例如:

// config.yml
my_listener:
   class: MyListener
   arguments: [@service_container]

my_service:
   class: MyService

// MyListener.php
class MyListener
{
    protected $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function myFunction()
    {
        $my_service = $this->container->get('my_service');
        $my_service->doSomething();
    }
}

那么它就像我一样工作:
// config.yml
my_listener:
   class: MyListener
   arguments: [@my_service]

my_service:
   class: MyService

// MyListener.php    
class MyListener
{
    protected $my_service;

    public function __construct(MyService $my_service)
    {
        $this->my_service = $my_service;
    }

    public function myFunction()
    {
        $this->my_service->doSomething();
    }
}

那么为什么我不应该只注入(inject)服务容器,并从我的类(class)中获取服务呢?

最佳答案

我为什么应该更喜欢注入(inject)服务的原因列表:

  • 你的类只依赖于它需要的服务,而不是服务容器。这意味着该服务可以在不使用 Symfony 服务容器的环境中使用。例如,你可以将你的服务变成一个可以在 Laravel、Phalcon 等中使用的库——你的类不知道依赖项是如何被注入(inject)的。
  • 通过在配置级别定义依赖关系,可以通过配置转储器了解哪些服务在使用哪些其他服务。例如,通过注入(inject) @mailer ,很容易从注入(inject)邮件程序的服务容器中计算出来。另一方面,如果您执行 $container->get('mailer') ,那么找出邮件程序在哪里使用的唯一方法就是执行 find
  • 在编译容器时,而不是在运行时,您将收到有关缺少依赖项的通知。例如,假设您定义了一个服务,并将其注入(inject)到监听器中。几个月后,您不小心删除了服务配置。如果您正在注入(inject)服务,您将在清除缓存后立即收到通知。如果你注入(inject)服务容器,你只会在监听失败时发现错误,因为容器无法获取服务。当然,如果您进行了全面的集成测试,您可以选择它,但是……您已经进行了全面的集成测试,不是吗? ;)
  • 如果你注入(inject)了错误的服务,你会很快知道。例如,如果您有:

    public function __construct(MyService $my_service)
    {
       $this->my_service = $my_service;
    }
    

    但是您已将监听器定义为:
    my_listener:
        class: Whatever
        arguments: [@my_other_service]
    

    当监听器接收到 MyOtherService 时,PHP 会抛出一个错误,告诉你它接收到了错误的类。如果您正在执行 $container->get('my_service'),则您假设容器正在返回正确的类,并且可能需要很长时间才能确定它不是。
  • 如果您使用的是 IDE,那么类型提示会增加一大堆额外的帮助。如果您使用的是 $service = $container->get('service');,那么您的 IDE 不知道 $service 是什么。如果你注入(inject)

    public function __construct(MyService $my_service)
    {
       $this->my_service = $my_service;
    }
    

    那么你的 IDE 就知道 $this->my_serviceMyService 的一个实例,并且可以提供方法名称、参数、文档等方面的帮助。
  • 您的代码更易于阅读。您的所有依赖项都在类的顶部定义。如果它们使用 $container->get('service') 分散在整个类(class)中,那么很难弄清楚。
  • 您的代码更容易进行单元测试。如果要注入(inject)服务容器,则必须模拟服务容器,并将模拟配置为返回相关服务的模拟。通过直接注入(inject)服务,您只需模拟服务并注入(inject)它们 - 您跳过了整个复杂层。
  • 不要被“它允许延迟加载”的谬论所迷惑。您可以 configure lazy loading at configuration level ,只需将服务标记为 lazy: true

  • 就个人而言,唯一一次注入(inject)服务容器是最好的解决方案是当我试图将安全上下文注入(inject)到一个原则监听器中时。这引发了循环引用异常,因为用户存储在数据库中。结果是原则和安全上下文在编译时相互依赖。通过注入(inject)服务容器,我能够绕过循环依赖。但是,这可能是代码异味,并且有一些方法可以绕过它(例如,通过 using the event dispatcher ),但我承认增加的复杂性可能超过好处。

    关于symfony - 在 Symfony2 中,为什么注入(inject)服务容器而不是单个服务是一个坏主意?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23931321/

    相关文章:

    javascript - 使用 Javascript 时出现 Symfony/Twig : trying to dynamically add rows via an Add Row button, 问题

    typescript - Nest 无法解决对循环依赖的服务依赖

    scala - 我将如何在 Scala 中注入(inject)一个模拟的单例对象?

    asp.net-mvc - 使用 Simpleinjector 为 AccountController 注册 Identity Framework UserStore

    rest - 在Silex/Symfony 2中没有模型的情况下验证POST数据?

    symfony - WebPack Encore 中缺少带有 CKEditor 的工具栏项目

    php - Symfony 2 'The process has been signaled with signal 5' 在使用 wkhtmltopdf 和 knpsnappy 时

    php - API 平台不可变属性

    android - 注入(inject) Otto 事件总线而不是使用静态单例的优势

    c# - ASP.NET Core 2.0 : Why doesn't services. AddDbContext 接受一个接口(interface)?