I’ve recently read that using the keyword “new” in a constructor is highly frowned upon, but I’m not sure I understand why? For example, how is:
class A {
public $foo;
function __construct() {
$this->foo = new Bar();
}
}
Any different from:
class A {
public function someMethod() {
$foo = new Bar();
}
}
???
This is really the theory behind dependency injection.
It’s not that using “new” is a bad idea, per se. Rather, by instantiating objects inside of your class your are creating hard dependencies which can never be changed or switched out without changing the class itself.
It also violates the paradigm of “coding to interface, not implementation”
Example:
Now, suppose one day you wanted to create an ATT, TMobile and Sprint phone? Surely they are all able to make calls, and can do so having just a phone number. Also, the phone class shouldn’t care who the carrier is, as it’s job is to facilitate entering a number – not to actually make a network connection, right?
So, that being said, we shouldn’t have to create a new
SprintPhoneclass that can instantiate another Sprint network object, right? Right.So what’s the better way?
Well now, you can just say:
$phone = new Phone(new Sprint())or$phone = new Phone(new Verizon())Also notice that our call to
distinctiveRingis gone. Why? Well, because we don’t know that any object implementing NetworkInterface will necessarily support a distinctive ring. But this is good because now ourPhonecan support ANYNetworkwith no code changes.If you want support for distinctive ring, you can always create a new interface that supports the
distinctiveRingmethod. In yourPhoneobject, you can check if yourNetworkimplementsDistinctiveRingerInterfaceand, if so, make your distinctive ring. But you’re no longer tied to a specific network with this approach. Better yet, you were forced to do this because you took the right approach from the start.And, any other network which can feasibly be created later down the road. More importantly, your class no longer has to care about what kind of Network object it’s given. It knows, (because it received an object implementing NetworkInterface), that the
Networkobject is able to make acallwith a$number.This also tends to lead to much better code, with a better separation of concerns.
And finally: testing.
With the first example, if you tried to test your Phone object, it was going to make a call on the Verizon network. Sucks to be the person getting called all day because you’re running unit tests, right? Right.
Well, simply create a TestNetwork class that implements NetworkInterface, and pass that to your phone object. Your TestNetwork class can do anything you want inside of it’s
callmethod – or do nothing.Furthermore, you can just create a mock object using
PHPUnit, and ensure that thecallmethod on your TestNetwork actually gets called. You couldn’t do this before because theNetworkwas being instantiated inside of yourPhone.