I don't blindly follow design patterns (we really just need to understand object communication IMO), but I also don't want to be ignorant of them either.
Is there a name for a design pattern (similar to the Delegation Pattern used here or perhaps some double-dispatchy-strategy-patterny thing-a-ma-bopper) but instead of rewriting each method and delegating, you would just pass a reference ($this) to yourself into the dependency? And is this a reasonable solution to have in a programmers tool box?
Need a practical example? Sure...Continue on...
Consider you are using a DI container and have perhaps some Controller (MVC) method like this:
Figure 1:
// Phew! Four dependencies injected here:
public function index(QueryManagerInterface $queryManager, BlogQueryInterface $blogQuery, RenderQueryPDFInterface $queryPDFRenderer, RequestInterface $request) {
// Do some "managing" of a query, then render it into a PDF
$queryManager->setQuery($query);
$queryManager->addInput($request->input());
$queryPDFRenderer->setQuery($query);
$output = $queryPDFRenderer->render();
return $output;
}
Now instead imagine your Controller method looks like this:
Figure 2:
// Nice! Just two dependencies!
public function index(BlogQueryInterface $blogQuery, RequestInterface $request) {
$blogQuery->getQueryManager()->addInput($request->input());
$output = $blogQuery->getPDFRenderer()->render();
return $output;
}
How did I do this? All the code is the same in all these classes, except I updated the class of $blogQuery:
Class BlogQuery Implements BlogQueryInterface, QueryManageableInterface, PDFRenderableInterface {
public function __construct(QueryManagerInterface $manager, $PDFRendererInterface $pdfRenderer){
// Here I pass a reference of this own class into its dependencies
$this->manager = $manager->setQuery($this);
$this->pdfRenderer = $pdfRenderer->setQuery($this);
}
public function getQueryManager() { return $this->manager; }
public function getPDFrenderer() { return $this->pdfRenderer; }
...
}
The pros of Figure 2 are:
What pattern did I use in Figure 2? What are the cons of such an approach? Is such an approach considered good oop practice?
This looks to me like the Visitor Pattern, where __construct
makes the QueryManagerInterface
and PDFRendererInterface
visitors to the BlogQuery
.
I don't want to get too involved in the pros and cons of programming patterns, because for the most part those will be primarily opinion-based. But, one pretty objective result of the visitor pattern is it tends to have more bouncing between files/classes than other options, which can make it more difficult for a reader to load into their head. Ultimately, "good OOP practice" comes down to experience, judgement, and probably rewriting it three times.
I do want to point out that you haven't really reduced the dependencies of your index
function. Yes, it takes fewer arguments, but it still knows how the QueryManagerInterface
and RenderQueryPDFInterface
behave. If those interfaces change (e.g., rename addInput
or render
), your code in index
also has to change. When you're testing index
, you still have to set up or mock those two objects for the test to pass. Unless you can hide that behavior inside the BlogQueryInterface
and RequestInterface
(see also the Law of Demeter), index
has exactly the same dependencies in both approaches.