避免在构造函数服务 Symfony 中使用长参数列表

问题描述 投票:0回答:1

我一直在使用 Symfony 来开发我的 Web 应用程序,但我一直遇到一个问题。我总是在服务的构造函数中遇到太多混乱,因为我希望能够正确地对我的服务进行单元测试。

理论用例

假设我需要一项服务来处理 XML 文件并将其内容保存到数据库中。

<?xml version="1.0" encoding="UTF-8" ?>
<users>
    <user>
        <id>1234</id>
        <username>Example User</username>
        <email>[email protected]</email>
        <usergroup>
            <id>567</id>
            <name>Example User Group</name>
        </usergroup>
        <permissions>
            <item>ALLOWED_TO_CREATE</item>
            <item>ALLOWED_TO_UPDATE</item>
            <item>ALLOWED_TO_DELETE</item>
            <item>ALLOWED_TO_view</item>
        </permissions>
    </user>
</users>

您已经想到了很多需要注入到此服务中的事情:

  • DomCrawler(读取 XML 文件)
  • UserRepository(获取现有用户)
  • UserGroupRepository(获取现有的用户组)
  • PermissionsRepository(获取现有权限)
  • EntityManager(持久刷新新/更新的对象)

我正在使用的真实 XML 文件包含更多数据,这需要我注入更多存储库和其他处理某些逻辑的服务。

解决方案1:使用Doctrine服务

将 Doctrine 服务目录注入我的服务并通过

$doctrine->getRepository(User::class)

获取存储库

优点

  • 显着减少争论量

缺点

  • 单元测试变得更加困难,因为我不知道服务访问什么,这使得它们变得无用

解决方案2:使用setter注入

从构造函数中删除所有服务和存储库并创建 setter 方法并在 services.yml 中调用然后

services:
  AppBundle\Service\MyImportService:
    calls:
      - [setUserRepository, ['@app.user_repository']]

优点

  • 可以说更容易阅读

缺点

  • 更多代码
  • 服务不再是强制性的,可能需要可选验证

问题

有哪些解决方案可以使我的服务中的参数列表在可读性和单元测试能力方面更易于维护?

有一长串的论点甚至被认为是不好的做法吗?

php symfony unit-testing dependency-injection
1个回答
8
投票

构造函数中包含许多参数是一种“代码味道”,称为“构造函数过度注入”。这种代码味道通常表明该类承担了过多的责任,这意味着它违反了单一职责原则(SRP)。违反 SRP 会导致维护问题,因此您应该密切关注它们。 尽管重构为属性注入可能会减少构造函数参数的数量,从而减少构造函数中的混乱程度,但它并没有解决根本问题,即此类变得过于复杂。因此,属性注入

不是

构造函数过度注入的解决方案。 解决这种代码味道的方法是降低手头类的复杂性。有很多方法可以做到这一点,但最常见的方法是Facade Services 重构

外观服务与参数对象

密切相关,但主要区别在于参数对象仅将参数移动到公共根,而外观服务
将聚合行为隐藏在新抽象背后

。虽然外观服务可能是由于纯粹的机械重构而开始其生命,但事实证明,提取的行为本身就代表了一个领域概念。 优点:

降低类的复杂性,从而解决根本问题

防止依赖 Doctrine 服务(这是
    服务定位器反模式
  • 的应用)
  • 引入了领域语言的新概念和改进。 防止属性注入(因为属性注入会导致
  • 时间耦合
  • 缺点:
Facade Services 可能不是正确的重构。存在许多替代方案,例如

Domain Events

依赖注入原则、实践和模式

中讨论的主题。例如,第 6.1 节专门讨论了从构造函数过度注入到外观服务或域事件的重构,而第 9 章和第 10 章则深入探讨了装饰器的使用。但请注意,本书的示例是用 C# 编写的,并且重点关注静态类型、面向对象的语言。虽然有几章完全关注 .NET 技术,但您会发现大多数其他部分也适用于其他语言,例如现代版本的 PHP。

© www.soinside.com 2019 - 2024. All rights reserved.