代码之家  ›  专栏  ›  技术社区  ›  Rupert Madden-Abbott

哪个更好:依赖注入+注册表还是依赖注入或全局注册表?

  •  16
  • Rupert Madden-Abbott  · 技术社区  · 16 年前

    首先,我想把这个问题限制在web开发上。所以这是语言不可知论,只要语言被用于web开发。就我个人而言,我是从PHP的背景来看这一点的。

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

    1. 使数据库对象成为全局对象,以便可以从任何地方访问它。
    2. 将数据库类以参数的形式传递给控制器类,例如,将参数传递给控制器的构造函数。这就是所谓的依赖注入(DI)。

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

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

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

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

    在讨论DI与Globals时经常提到的一点是,Globals会抑制您正确测试程序的能力。globals究竟是如何阻止我测试DI不支持的程序的?我在许多地方读到,这是因为一个全局可以从任何地方改变,因此很难嘲笑。然而,在我看来,至少在PHP中,对象是通过引用传递的,因此在某个类中更改注入的对象也会在任何其他类中更改它。

    2 回复  |  直到 16 年前
        1
  •  13
  •   Community Mohan Dere    9 年前

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

    首先,registry类将包含大量的递归

    Registry 类转换为数据库类。你也可以 have dedicated methods on the Registry to create the required classes

    请注意,全局注册表不会受到此问题的影响,因为它不会被传递到任何对象中。

    注册表本身可能没有递归,但是注册表中的对象很可能有循环引用。当使用5.3之前的PHP版本从注册表中清除对象时,这可能会导致内存泄漏 Garbage Collector

    是的。但这就是注册的目的。它与向对象传递$全局变量没有太大区别。如果您不想这样做,就不要使用注册表,而只传递类实例处于有效状态所需的参数。或者干脆不储存。

    我可以做$this->注册表->路由器->注册表->数据库

    database $this router ,但你可以 数据库 直接。当然。是个登记处。这就是你写它的目的。如果要将注册表存储在对象中,可以将它们包装到一个隔离的接口中,该接口只允许访问其中包含的数据的子集。

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

    不一定。使用构造函数注入时,可以将参数的数量限制为使对象进入有效状态所必需的参数。其余的可选依赖项也可以通过setter注入来设置。而且,没有人阻止您在数组或配置对象中添加参数。或使用 Builders .

    考虑到这两个版本的DI都存在这些问题,全局注册不是更好吗?在DI上使用全局注册表会损失什么?

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

    在讨论DI与Globals时经常提到的一点是,Globals会抑制您正确测试程序的能力。globals究竟是如何阻止我测试DI不支持的程序的?

    They just make it harder. dependencies on the global state ,则必须在每次测试运行时创建此状态。

    Dependency Hell .

        2
  •  5
  •   ircmaxell    16 年前

    我会把这个作为一个答案,因为我想包括代码。

    我比较了传递对象和使用 global

    结果是:

    Passed Completed in 0.19198203086853 Seconds
    Globaled Completed in 0.20970106124878 Seconds
    

    如果我去掉嵌套对象和自引用,结果是一样的。。。

    所以是的,这两种不同的数据传递方法之间似乎没有真正的性能差异。因此,做出更好的架构选择(IMHO,这就是依赖注入)。。。

    剧本:

    $its = 10000;
    $bar = new stdclass();
    $bar->foo = 'bar';
    $bar->bar = $bar;
    $bar->baz = new StdClass();
    $bar->baz->ar = 'bart';
    
    $s = microtime(true);
    for ($i=0;$i<$its;$i++) passed($bar);
    $e = microtime(true);
    echo "Passed Completed in ".($e - $s) ." Seconds\n";
    
    $s = microtime(true);
    for ($i=0;$i<$its;$i++) globaled();
    $e = microtime(true);
    echo "Globaled Completed in ".($e - $s) ." Seconds\n";
    
    function passed($bar) {
        is_object($bar);
    }
    
    function globaled() {
        global $bar;
        is_object($bar);
    }
    

    按5.3.2测试

    推荐文章