这实际上是依赖项注入背后的理论。
并不是说使用“ new”本身是一个坏主意。相反,通过实例化类内部的对象,您正在创建硬依赖性,如果不更改类本身就无法更改或切换出来。
它也违反了“编码到接口,而不是实现”的范式
示例: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手机?当然,他们所有人都可以打电话,而且只要有电话号码就可以打电话。另外,电话班级不必关心运营商是谁,因为它的工作是方便输入号码-而不是实际建立网络连接,对吗?
因此,我们不必创建一个可以实例化另一个Sprint网络对象的新SprintPhone类,对吗?对。
那还有什么更好的方法?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 implements DistinctiveRingerInterface,如果是,则制作独特的戒指。但是,您不再需要使用这种方法来绑定特定的网络。更好的是,您被迫这样做,因为从一开始就采取了正确的方法。
而且,以后可以创建任何其他可行的网络。更重要的是,您的班级不再需要关心它所提供的哪种网络对象。它知道(因为它接收到一个实现NetworkInterface的对象),所以Network对象能够用call来创建$number。
这也往往会导致代码更好,关注点分离更好。
最后是测试。
在第一个示例中,如果您尝试测试Phone对象,它将在Verizon网络上进行呼叫。很糟,因为您正在运行单元测试,所以整天都被打来电话,对吧?对。
好,只需创建一个实现NetworkInterface的TestNetwork类,然后将其传递给您的电话对象。您的TestNetwork类可以在call方法中执行任何您想执行的操作-或不执行任何操作。
此外,您可以只使用PHPUnit创建一个模拟对象,并确保您的TestNetwork上的call方法实际上得到了调用。您以前无法执行此操作,因为Network已在Phone内部实例化。