这两种php构造函数注入方式有何区别?

7

我希望你能帮助我翻译一下 PHP 中的依赖注入,我发现在 Laravel 中有两种方法可以实现。

假设我有一个名为 Foo 的类:

class Foo{

}

现在我有一个名为 Bar 的类,它依赖于 Foo,因此我可以这样做:

class Bar{
    protected $foo;
    public function __construct()
    {
        $this->foo = new Foo();
    }
}

但在Laravel中,我遇到了像类型提示和反射这样的术语,使我能够做到这一点:

class Bar{
    protected $foo;
    public function __construct(Foo $foo)
    {
        $this->foo = $foo;
    }
}

我想要理解的是这两者之间的区别。它们完全相同吗?我应该更喜欢其中的一个吗?
注:我是新手,不确定在问题中是否正确使用了行话。

9
第一个例子很难测试,因为你无法在 Bar 内部模拟 Foo;第二个例子被称为依赖注入(DI),它允许你在测试 Bar 时模拟 Foo。 - Mark Baker
3
可能是什么是依赖注入?的重复问题。 - Gal Sisso
5
第二个选择消除了对于Foo必须是一个Foo实例的限制,但允许它可以是任何扩展自Foo类的实例 (类型提示会强制要求这一点) ... 这样做实现了松耦合,因为你不再局限于实际的Foo实例。 - Mark Baker
2
@MarkBaker:你的意思是说,如果我编写了一个接口并且有多个实现,控制器不需要知道我将使用哪个实现,我只需对接口进行类型提示,这样做会有所帮助。我的理解正确吗? - Rohan
你理解得很正确:在 DI 中不需要类型提示;但是提示接口以实现松耦合正是类型提示的价值所在…… DI 本身就有其价值,使类更易于测试,但并不需要类型提示;但是通过类型提示实现松耦合也是必要的一步。 - Mark Baker
显示剩余6条评论
1个回答

1
大多数情况下,这归结于代码的耦合性
class Foo {
    public function __construct() {
        new Bar;
    }
}

这将一个非常特定的Bar与这个特定的Foo配对。没有办法在不重写此代码的情况下更改要实例化的哪个Bar。这也意味着Foo需要了解Bar的依赖关系。也许今天可以只用new Bar来实例化Bar。但是也许明天您正在重构Bar,现在必须使用new Bar($database)来实例化它。现在您还需要重写Foo以适应此更改。
这就是依赖注入的作用(上面不是依赖注入,您没有“注入”任何内容)。
class Foo {
    public function __construct(Bar $bar) { }
}

这个 Foo 只是在实例化时声明需要具有 Bar 特征的对象。但是 Foo 不需要知道 Bar 是如何产生的,它的依赖关系是什么或者它确切地做了什么。它对 Bar 的唯一期望是一个定义好的 public 接口,其他关于它的内容都是无关紧要的。事实上,为了获得更大的灵活性,您可能想在此处使用接口而不是具体类的依赖项。
依赖注入允许您将类的具体细节与其他代码分离。它允许您拥有一个中心位置,用于实例化类,这是您需要了解和考虑正在实例化的类的具体细节的位置。例如,这可以是一个依赖注入容器。您不希望将类实例化逻辑分散到各个地方,因为如上所述,该逻辑可能会发生变化,然后您需要在各个地方重写代码。
require_once 'Foo.php';
require_once 'Bar.php';

$foo = new Foo(new Bar);

上面的代码是决定将哪个Bar注入Foo的地方。它也是需要关注Bar依赖关系的地方。请注意,此代码仅执行依赖项加载和实例化。可以轻松更改此代码,而无需触及充满复杂业务逻辑的FooBar
依赖注入代码还允许您灵活地拆分和组合应用程序。例如,用于测试目的。或者只是在不同的上下文中灵活地重用不同的组件。
另请参见如何避免使用静态方法影响可测试性

非常感谢您提供这么详细的答案,我很感激。 :) - Rohan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接