我一直在使用 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>
您已经想到了很多需要注入到此服务中的事情:
我正在使用的真实 XML 文件包含更多数据,这需要我注入更多存储库和其他处理某些逻辑的服务。
将 Doctrine 服务目录注入我的服务并通过
$doctrine->getRepository(User::class)
获取存储库
优点
缺点
从构造函数中删除所有服务和存储库并创建 setter 方法并在 services.yml 中调用然后
services:
AppBundle\Service\MyImportService:
calls:
- [setUserRepository, ['@app.user_repository']]
优点
缺点
有哪些解决方案可以使我的服务中的参数列表在可读性和单元测试能力方面更易于维护?
有一长串的论点甚至被认为是不好的做法吗?
构造函数中包含许多参数是一种“代码味道”,称为“构造函数过度注入”。这种代码味道通常表明该类承担了过多的责任,这意味着它违反了单一职责原则(SRP)。违反 SRP 会导致维护问题,因此您应该密切关注它们。 尽管重构为属性注入可能会减少构造函数参数的数量,从而减少构造函数中的混乱程度,但它并没有解决根本问题,即此类变得过于复杂。因此,属性注入
不是构造函数过度注入的解决方案。 解决这种代码味道的方法是降低手头类的复杂性。有很多方法可以做到这一点,但最常见的方法是Facade Services 重构:
外观服务与参数对象
密切相关,但主要区别在于参数对象仅将参数移动到公共根,而外观服务将聚合行为隐藏在新抽象背后降低类的复杂性,从而解决根本问题
中讨论的主题。例如,第 6.1 节专门讨论了从构造函数过度注入到外观服务或域事件的重构,而第 9 章和第 10 章则深入探讨了装饰器的使用。但请注意,本书的示例是用 C# 编写的,并且重点关注静态类型、面向对象的语言。虽然有几章完全关注 .NET 技术,但您会发现大多数其他部分也适用于其他语言,例如现代版本的 PHP。