我最近读到在构造函数中使用关键字"new"非常不满意,但我不确定我理解为什么?例如,如何:
class A {
public $foo;
function __construct() {
$this->foo = new Bar();
}
}
Run Code Online (Sandbox Code Playgroud)
任何不同于:
class A {
public function someMethod() {
$foo = new Bar();
}
}
Run Code Online (Sandbox Code Playgroud)
???
Col*_*n M 13
这实际上是依赖注入背后的理论.
根据说,使用"新"并不是一个坏主意.相反,通过实例化类中的对象,您将创建硬依赖项,在不更改类本身的情况下永远不会更改或切换.
它也违反了"编码到接口而不是实现"的范例
例:
class Phone {
protected $network;
public function __construct() {
$this->network = new Verizon();
$this->network->distinctiveRing();
}
}
class Verizon {
public function call($number) {
....
}
public function distinctiveRing() {
}
}
Run Code Online (Sandbox Code Playgroud)
现在,假设有一天你想创建一个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 {
...
}
Run Code Online (Sandbox Code Playgroud)
那么现在,你可以说:$phone = new Phone(new Sprint())或者$phone = new Phone(new Verizon())
还要注意我们的电话distinctiveRing已经消失了.为什么?好吧,因为我们不知道实现NetworkInterface的任何对象都必然支持一个独特的环.但这很好,因为现在我们Phone可以支持ANY Network而不需要更改代码.
如果您需要支持特色铃声,您可以随时创建支持该distinctiveRing方法的新界面.在您的Phone对象中,您可以检查您的Network 工具 DistinctiveRingerInterface,如果是,请制作您的特色戒指.但是,您不再使用此方法与特定网络绑定.更好的是,你被迫这样做是因为你从一开始就采取了正确的方法.
而且,任何其他网络都可以在以后的路上创建.更重要的是,您的班级不再需要关心它所提供的网络对象类型.它知道,(因为它收到了一个实现NetworkInterface的对象),该Network对象能够call使用a $number.
这也可以带来更好的代码,更好地分离关注点.
最后:测试.
在第一个示例中,如果您尝试测试Phone对象,它将在Verizon网络上进行调用.因为你正在进行单元测试,所以整天都被叫到了,对吧?对.
好吧,只需创建一个实现NetworkInterface的TestNetwork类,并将其传递给您的手机对象.您的TestNetwork类可以在其call方法中执行任何操作 - 或者不执行任何操作.
此外,您可以使用创建模拟对象PHPUnit,并确保call实际调用TestNetwork上的方法.之前你不能这样做,因为它Network是在你的内部实例化的Phone.