Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 使用;新";构造函数中的关键字_Php_Oop - Fatal编程技术网

Php 使用;新";构造函数中的关键字

Php 使用;新";构造函数中的关键字,php,oop,Php,Oop,我最近读到,在构造函数中使用关键字“new”是非常不受欢迎的,但我不知道我是否理解为什么?例如,如何: class A { public $foo; function __construct() { $this->foo = new Bar(); } } 任何不同于: class A { public function someMethod() { $foo = new Bar(); } } ???在第一个示例中

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

class A {
    public $foo;

    function __construct() {
        $this->foo = new Bar();
    }
}
任何不同于:

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

???

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

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

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

您的问题和代码示例似乎没有太多共享

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

class A {
    public $foo;

    function __construct() {
        $this->foo = new Bar();
    }
}
第二个示例将其分配给_construct()方法的一个局部变量,因此您以后将没有机会检索该值:

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

此外:使用新的通常是好的。然而,您可能已经读过控制反转或IOC,这是一种避免依赖性的技术,因此您会尝试避免直接在构造函数中创建类,例如,请参见这实际上是依赖性注入背后的理论

从本质上来说,使用“新”并不是一个坏主意。相反,通过实例化类内部的对象,您将创建硬依赖项,这些硬依赖项在不更改类本身的情况下永远无法更改或切换

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

例如:

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())

还请注意,我们对
的调用已消失。为什么?因为我们不知道任何实现NetworkInterface的对象都必须支持一个独特的环但这很好,因为现在我们的
手机
可以支持任何
网络
,而无需更改代码

如果您想支持Distingive ring,您可以创建一个支持
Distingitive
方法的新接口。在您的
手机
对象中,您可以检查您的
网络
是否实现了
DistributiverIngerInterface
,如果实现了,则制作您的独特铃声。但通过这种方法,您不再局限于特定的网络。更好的是,你被迫这样做,因为你从一开始就采取了正确的方法

以及任何其他可以在以后创建的网络。更重要的是,您的类不再需要关心它所提供的网络对象的类型。它知道(因为它收到了一个实现NetworkInterface的对象),
网络
对象能够使用
$number
进行
调用

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

最后一点:测试

在第一个例子中,如果你试图测试你的手机对象,它会在Verizon网络上打电话。因为你在运行单元测试而整天被打电话的人很糟糕,对吧?对

那么,只需创建一个实现NetworkInterface的TestNetwork类,并将其传递给您的phone对象。您的TestNetwork类可以在它的
调用
方法中做任何您想做的事情,或者什么都不做


此外,您只需使用
PHPUnit
创建一个模拟对象,并确保TestNetwork上的
call
方法实际被调用。您以前不能这样做,因为
网络
正在您的
手机中实例化

您在哪里读到过?一定有一些重要的背景。阅读控制反转或IOC,也可以看到我在这方面的答案。一个原因是它使您的类更难进行单元测试:您将如何模拟Bar的实例?不要认为这真的回答了为什么在构造函数中实例化新对象会受到反对。我不知道为什么会是…@JonStirling,因为控制反转,见下面我的答案。
因此,尽管如此,我们不应该创建一个新的SprintPhone类来实例化另一个Sprint网络对象。
。嘿,嘿,告诉几乎所有的移动运营商。我只想补充一点关于“编码到接口,而不是实现”。虽然我同意您在使用
new Foo()
时要小心,但我不同意您仅仅在另一个类中使用new Foo()违反了这一原则。如果这是真的,我们会注射所有我们不做的东西。“向接口编码”更像是一个抽象概念,即您应该首先考虑您的接口,并且您应该为这些接口键入提示,而不是实现。这并不是说你不应该在类中使用
newfoo()
,我想说的是,一般来说,你不应该这样做。当然,这完全是个人的选择。但是如果你在一个类中使用“newfoo”,你只会使测试代码或将来进行更改变得更加困难。当然,这并不总是错误的,但它被滥用到了值得警惕的程度。