There is a small problem that I sometimes encounter when in a class I use an object (dependency) that needs to be instantiated fresh - that is within the calling class and not earlier like in a container. Let’s take a first example, which is quite obvious and simple - there is ActionsChecker
class which is responsible for checking if certain actions can be performed on an order:
class ActionsChecker {
public function canBeSent($order_id):bool {
// ...
}
public function canBeCancelled($order_id):bool {
// ...
}
}
And the class which submits an order (like from a form) uses ActionsChecker
like this:
class OrderSubmission {
private $actionsChecker;
public function __construct(ActionsChecker $actionsChecker) {
$this->actionsChecker = $actionsChecker;
}
public function sendOrder($order_id) {
if (!$this->actionsChecker->canBeSent($order_id)) {
throw new Exception("Can't send order");
}
// send order...
}
public function cancelOrder($order_id) {
if (!$this->actionsChecker->canBeCancelled($order_id)) {
throw new Exception("Can't cancel order");
}
// cancel order...
}
}
This is nice and ActionsChecker
is instantiated somewhere else in a container and the OrderSubmission
class doesn’t have to deal with it.
Now suppose I decide to change ActionsChecker
because I come to a conclusion that there is no need to accept $order_id
in every method and instead I will require it in the constructor (it may simplify the class or I may have other reasons for this):
class ActionsChecker {
private $order_id;
public function __construct($order_id) {
$this->order_id = $order_id;
}
public function canBeSent():bool {
// ...
}
public function canBeCancelled():bool {
// ...
}
}
Now I don’t need to pass $order_id
to every method but instead I need to have the object created already for this specific order. A container can’t do it beforehand because $order_id
is not known before OrderSubmission
is used. A working version using new
keyword would look like this:
class OrderSubmission {
public function sendOrder($order_id) {
$actionsChecker = new ActionsChecker($order_id);
if (!$actionsChecker->canBeSent($order_id)) {
throw new Exception("Can't send order");
}
// send order...
}
public function cancelOrder($order_id) {
$actionsChecker = new ActionsChecker($order_id);
if (!$actionsChecker->canBeCancelled($order_id)) {
throw new Exception("Can't cancel order");
}
// cancel order...
}
}
Instantiating objects in the class doesn’t look good to me, what other option do I have? The only thing I can think of is pass a factory object to OrderSubmission
and then I would do something like this:
$actionsChecker = $this->actionsCheckerFactory->create($order_id);
which is somewhat fine but feels like a lot of boilerplate and I must admit I am often reluctant to make my dependencies require arguments in constructor (like $order_id
) just because I’d have to deal with factories (create them, instantiate them and then pass them to class constructors), even when requiring such arguments would make the class (dependency) cleaner.
What are your thoughts about it and what is the most elegant solution? Should I go with a factory and accept the additional code?