php - 哪个更好 : Dependency Injection+Registry or Dependency Injection or Global Registry?

标签 php dependency-injection registry global-variables

首先,我想将此问题仅限于 Web 开发。因此,只要该语言用于 Web 开发,这就是语言不可知的。就个人而言,我是从 PHP 背景而来的。

通常我们需要使用来自多个作用域的对象。例如,我们可能需要在正常范围内使用数据库类,但也需要从 Controller 类中使用。如果我们在正常范围内创建数据库对象,那么我们无法从 Controller 类内部访问它。我们希望避免在不同的范围内创建两个数据库对象,因此需要一种无论范围如何都可以重用数据库类的方法。为此,我们有两个选择:

  • 将数据库对象设为全局,以便可以从任何地方访问。
  • 将数据库类以例如 Controller 构造函数的参数的形式传递给 Controller ​​类。这称为依赖注入(inject) (DI)。

  • 当有许多类涉及许多不同范围内的所有要求对象时,问题变得更加复杂。在这两种解决方案中,这都会成为问题,因为如果我们将每个对象都设置为全局对象,则会在全局范围内放置太多干扰,并且如果我们将太多参数传递给一个类,该类将变得更加难以管理。

    因此,在这两种情况下,您经常会看到注册表的使用。在全局情况下,我们有一个注册表对象,它被设置为全局,然后将我们所有的对象和变量添加到其中,使它们在任何对象中可用,但只将一个变量,注册表,放入全局范围内。在 DI 的情况下,我们将注册表对象传递给每个类,将参数数量减少到 1。

    就我个人而言,我使用后一种方法是因为很多文章都提倡使用全局变量,但我遇到了两个问题。首先,注册表类将包含大量递归。例如,注册表类将包含数据库类所需的数据库登录变量。因此,我们需要将注册表类注入(inject)到数据库中。但是,许多其他类将需要该数据库,因此需要将数据库添加到注册表中,从而创建一个循环。现代语言可以处理这个问题还是会导致巨大的性能问题?请注意,全局注册表不会受到此影响,因为它不会传递到任何内容中。

    其次,我将开始将大量数据传递给不需要它的对象。我的数据库不关心我的路由器,但路由器将与数据库连接详细信息一起传递给数据库。这通过递归问题变得更糟,因为如果路由器有注册表,注册表有数据库和注册表,并且注册表被传递到数据库,那么数据库将通过路由器传递给自身(即我可以做 $this->registry->router->registry->database从数据库类内部`)。

    此外,除了更多的复杂性之外,我没有看到 DI 给我带来了什么。我必须向每个对象传递一个额外的变量,并且必须使用带有 $this->registry->object->method() 而不是 $registry->object->method() 的注册表对象。现在这显然不是一个大问题,但如果它没有给我任何关于全局方法的东西,它似乎没有必要。

    显然,当我在没有注册表的情况下使用 DI 时,这些问题不存在,但是我必须“手动”传递每个对象,从而导致类构造函数具有大量参数。

    鉴于两个版本的 DI 都存在这些问题,全局注册表不是更好吗?通过 DI 使用全局注册表会丢失什么?

    在讨论 DI 与全局变量时经常提到的一件事是全局变量会抑制您正确测试程序的能力。全局变量究竟如何阻止我测试 DI 不会的程序?我在很多地方都读到过,这是因为全局变量可以从任何地方更改,因此很难模拟。但是,在我看来,至少在 PHP 中,对象是通过引用传递的,因此在某个类中更改注入(inject)的对象也会在已注入(inject)它的任何其他类中更改它。

    最佳答案

    让我们一一解决这个问题。

    Firstly, the registry class will contain huge amounts of recursion



    您不必注入(inject) Registry类进入数据库类。你也可以have dedicated methods on the Registry to create the required classes为你。或者,如果您注入(inject)注册表,您可以简单地不存储它,而只能从中获取正确实例化类所需的内容。没有递归。

    Notice that the global registry does not suffer from this as it is not passed into anything.



    注册表本身可能没有递归,但注册表中的对象很可能具有循环引用。当 Garbage Collector 5.3 之前的 PHP 版本从注册表中取消设置对象时,这可能会导致内存泄漏。不会正确收集那些。

    Secondly, I will start passing large amounts of data to objects that don't need it. My database doesn't care about my router but the router will get passed to the database along with the database connection details.



    真的。但这就是注册表的用途。这与将 $_GLOBALS 传递到您的对象中没有太大区别。如果您不希望那样,请不要使用注册表,而只传递类实例处于有效状态所需的参数。或者干脆不存储它。

    I could do $this->registry->router->registry->database



    路由器不太可能公开获取注册表的公共(public)方法。您将无法访问 database来自 $this通过 router ,但您将能够访问 database直接地。当然。这是一个注册表。这就是你写它的目的。如果您想将注册表存储在您的对象中,您可以将它们包装到一个只允许访问其中包含的数据子集的隔离接口(interface)中。

    Obviously, these problems don't exist when I use DI without a registry but then I have to pass every object 'manually', resulting in class constructors with a ridiculous number of parameters.



    不必要。使用构造函数注入(inject)时,您可以将参数的数量限制为将对象置于有效状态所绝对必要的数量。其余的可选依赖项也可以通过 setter 注入(inject)来设置。此外,没有人会阻止您在 Array 或 Config 对象中添加参数。或使用 Builders .

    Given these issues with both versions of DI, isn't a global registry superior? What am I losing by using a global registry over DI?



    当您使用全局注册表时,您将此依赖项与类紧密耦合。这意味着如果没有这个具体的 Registry 类,就不能再使用 using 类了。您假设只有这个 Registry 而没有不同的实现。注入(inject)依赖项时,您可以自由注入(inject)满足依赖项责任的任何内容。

    One thing that is often mentioned when discussing DI vs Globals is that globals inhibit your ability to test your program properly. How exactly do globals prevent me from testing a program where DI would not?



    它们不会阻止您测试代码。 They just make it harder.进行单元测试时,您希望系统处于已知且可重现的状态。如果您的代码有 dependencies on the global state ,您必须在每次测试运行时创建此状态。

    I have read in many places that this is due to the fact that a global can be altered from anywhere and thus is difficult to mock



    正确,如果一个测试改变了全局状态,如果你不把它改回来,它可能会影响下一个测试。这意味着除了将被测对象设置为已知状态之外,您还必须努力重新创建环境。如果只有一个依赖项,这可能很容易,但是如果有很多依赖项,而且这些依赖项也依赖于全局状态呢?你最终会在 Dependency Hell .

    关于php - 哪个更好 : Dependency Injection+Registry or Dependency Injection or Global Registry?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3525238/

    相关文章:

    c# - 注入(inject)的依赖项应该公开访问还是私有(private)?

    c# - .Net Core - IConfigurationRoot 读取整个 json 文件

    php - 如何使 JSON 数据源适合 Jquery Autocomplete 小部件?

    php - Zend Form isValid() 返回 false

    hibernate - 使用 EJB 注入(inject) EntityManager

    powershell - IE 浏览器 - Powershell 脚本,用于将站点添加到受信任站点列表、禁用保护模式并降低所有区域的安全级别

    私有(private) docker 存储库的 ssl 证书

    php - 我如何使用mysql进行关键字搜索

    php - 按子数组中的多个值对 PHP 数组进行排序

    powershell - 使用powershell获取注册表项中特定字符串值的值