4

我最近读到在构造函数中使用关键字“new”是非常不受欢迎的,但我不确定我明白为什么?例如,如何:

class A {
    public $foo;

    function __construct() {
        $this->foo = new Bar();
    }
}

任何不同于:

class A {
    public function someMethod() {
        $foo = new Bar();
    }
}

???

4

3 回答 3

14

这确实是依赖注入背后的理论。

这并不是说使用“新”本身就是一个坏主意。相反,通过在类中实例化对象,您正在创建硬依赖项,如果不更改类本身,就永远无法更改或切换这些依赖项。

它也违反了“编码到接口,而不是实现”的范式

例子:

class Phone {
    protected $network;

    public function __construct() {
        $this->network = new Verizon();
        $this->network->distinctiveRing();
    }
}

class Verizon {
    public function call($number) {
        ....
    }

    public function distinctiveRing() {

    }
}

现在,假设有一天您想创建一部 ATT、TMobile 和 Sprint 手机?当然,他们都可以拨打电话,而且只需一个电话号码即可。此外,电话类不应该关心运营商是谁,因为它的工作是方便输入号码 - 而不是实际建立网络连接,对吗?

所以,话虽如此,我们不应该创建一个SprintPhone可以实例化另一个 Sprint 网络对象的新类,对吧?对。

那么更好的方法是什么?

class Phone {
    protected $network;

    public function __construct(NetworkInterface $network) {
        $this->network = $network;
    }
}

interface NetworkInterface {
    public function call($number);
}

class Verizon implements NetworkInterface {
    ...
}

class Sprint implements NetworkInterface {
    ...
}

现在好了,你可以说:$phone = new Phone(new Sprint())或者$phone = new Phone(new Verizon())

另请注意,我们的调用distinctiveRing已消失。为什么?好吧,因为我们不知道任何实现 NetworkInterface 的对象都必然支持独特的环。但这很好,因为现在我们Phone可以支持ANY Network而无需更改代码。

如果你想支持独特的铃声,你总是可以创建一个支持该distinctiveRing方法的新接口。在您的Phone对象中,您可以检查您的Network 工具 DistinctiveRingerInterface是否,如果是,则制作您独特的戒指。但是通过这种方法,您不再依赖于特定的网络。更好的是,您被迫这样做,因为您从一开始就采取了正确的方法。

并且,任何其他可以在以后创建的网络。更重要的是,你的类不再需要关心它给出了什么样的 Network 对象。它知道,(因为它接收到一个实现 NetworkInterface 的对象),该Network对象能够call使用 a 来制作 a $number

这也往往会导致更好的代码,更好地分离关注点。

最后:测试。

对于第一个示例,如果您尝试测试 Phone 对象,它将在 Verizon 网络上进行呼叫。因为你正在运行单元测试,所以整天被打电话的人很糟糕,对吧?对。

好吧,只需创建一个实现 NetworkInterface 的 TestNetwork 类,并将其传递给您的手机对象。您的 TestNetwork 类可以在它的call方法中做任何您想做的事情 - 或者什么也不做。

此外,您可以使用 创建一个模拟对象PHPUnit,并确保call您的 TestNetwork 上的方法实际被调用。你以前不能这样做,因为在Network你的Phone.

于 2013-01-08T17:01:15.767 回答
3

看来您的问题和您的代码示例并没有分享太多?

您的第一个代码示例应该可以工作,因为您分配给一个类变量:

class A {
    public $foo;

    function __construct() {
        $this->foo = new Bar();
    }
}

您的第二个示例将其分配给 __construct() 方法的本地变量,因此您以后将没有机会检索该值:

class A {
    public function someMethod() {
        $foo = new Bar();
    }
}

此外:使用 new 通常没问题。但是,您可能已经阅读过有关控制反转或 IOC 的信息,这是一种避免依赖关系的技术,因此您会尝试避免直接在构造函数中创建类,例如参见http://ralphschindler.com/2011/05 /18/learning-about-dependency-injection-and-php

于 2013-01-08T16:53:44.757 回答
1

在第一个示例中, $foo 可用于此类中的任何方法以及对象之外,
因此您可以执行以下操作:

$a = new A();
$a->foo->sth;

在第二个示例中,$foo 仅在 someMethod 中可用。

于 2013-01-08T16:52:17.220 回答