Использование ключевого слова " new " в конструкторах
Недавно я прочитал, что использование ключевого слова " new " в конструкторе сильно осуждается, но я не уверен, что понимаю, почему? Например, как это:
class A {
public $foo;
function __construct() {
$this->foo = new Bar();
}
}
Отличается от:
class A {
public function someMethod() {
$foo = new Bar();
}
}
???
3 ответа:
Это действительно теория инъекции зависимостей.
Дело не в том, что использование "нового" - плохая идея, как говорят. Скорее, создавая экземпляры объектов внутри класса, вы создаете жесткие зависимости, которые никогда не могут быть изменены или отключены без изменения самого класса.
Это также нарушает парадигму "кодирования для интерфейса, а не реализации"
Пример:
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
может поддерживать любойNetwork
без изменений кода.Если вам нужна поддержка distinctive ring, вы всегда можете создать новый интерфейс, поддерживающий метод
distinctiveRing
. В вашем объектеPhone
Вы можете проверить, если вашNetwork
реализуетDistinctiveRingerInterface
и, если это так, сделайте свой отличительный перстень. Но при таком подходе вы больше не привязаны к конкретной сети. А еще лучше, Вы были вынуждены сделать это, потому что с самого начала выбрали правильный подход.И, любая другая сеть, которая может быть осуществимо создана позже по дороге. Что еще более важно, ваш класс больше не должен заботиться о том, какой сетевой объект ему дан. Он знает (потому что он получил объект, реализующий NetworkInterface), что объект
Network
способен сделатьcall
С$number
.Это также приводит к гораздо лучшему коду, с лучшим разделением проблем.
И наконец: тестирование.
В первом примере, если вы попытаетесь протестировать объект телефона, он будет совершать вызов в сети Verizon. Хреново быть человеком, которому звонят весь день, потому что ты проводишь модульные тесты, верно? Правильно.
Ну, просто создайте класс TestNetwork, который реализует NetworkInterface, и передайте его на свой телефон объект. Ваш класс TestNetwork может делать все, что вы хотите, внутри своего метода
Кроме того, вы можете просто создать макет объекта с помощьюcall
- или ничего не делать.PHPUnit
и убедиться, что методcall
В вашей тестовой сети действительно вызывается. Вы не могли сделать этого раньше, потому чтоNetwork
был создан экземпляр внутри вашегоPhone
.
Похоже, что ваш вопрос и ваш пример кода не слишком похожи?
Ваш первый пример кода должен работать, потому что вы присваиваете переменной класса:
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