Search code examples
phpoopdesign-patternsdelegation

Is there a name for this design pattern (pass yourself into one of your own dependencies), and what are the pitfalls of such a technique?


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:

  • Less dependencies on Controller method
  • Less lines of code
  • Composition over inheritance (more loosely coupled?).
  • No need to "type a bunch of repetitive methods" like when using the Delegation pattern.
  • More understandable. Injecting just BlogQuery and the Request seems to capture the main context. (subjective)

What pattern did I use in Figure 2? What are the cons of such an approach? Is such an approach considered good oop practice?


Solution

  • 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.