When you browse for the phrase "constructors must not do work", then in various blog posts you will find the advice to not let the constructor do work. Despite this, I am having some trouble understanding why this is the case. Additionally, this popular post suggests to take such advice with a grain of salt.
I have an example of two implementations of the same situation. In the situation, a AFactory
has a method createA
, using a B
. A
needs a query result, that B
produces. There are two ways to implement this:
Example 1:
class AFactory {
public function createA(B $b): A {
return new A($b->getQueryResult());
}
}
class A {
private $query_result;
public function __construct(array $query_result) {
$this->query_result = $query_result;
}
public function doFooWithQueryResult() {
// Do something with query result
}
public function doBarWithQueryResult() {
// Do something with query result
}
}
In the first example, the factory fetches the query result and passes it to A
's constructor. A
then merely assigns the query result to the corresponding class property. However, there is one problem here: A
does not verify if the query result is a valid data structure, i.e. an actual query result suited for A
. It does not know where it came from. The responsibility for this validation has now leaked to the AFactory
, and A
has become very tightly coupled to AFactory
. The other implementation resolves this issue, but then the constructor performs work. And apparently that is bad.
Example 2:
class AFactory {
public function createA(B $b): A {
return new A($b);
}
}
class A {
private $query_result;
public function __construct(B $b) {
$this->query_result = $b->getQueryResult();
}
public function doFooWithQueryResult() {
// Do something with query result
}
public function doBarWithQueryResult() {
// Do something with query result
}
}
A
is some general type with simple type dependencies then it's acceptable as well imo. Note that often structurally correct data might trun out to be invalid (www.example.com
domain might not exist) and you'll find that out at "runtime" anyway.this
outside the constructor, you may have just written a memory leak (especially if an exception is thrown afterwards).