4

I'm trying to sharpen up my programming skills and I came upon a frustrating problem, which will probably be best explained with an example:

Let's say I'm creating a microCMS in PHP. This microCMS has a Router class, which is responsible for routing. It also holds the URI and extra parameters that are extracted from it.

class Router{
  private $uri;
  private $params;
  ...
  public function getRoute(){ ... }
  ...
  public function getParams(){
    return $this->params;
  }
  ...
}

I also have a Front Controller, to which I'm passing a new Router() object. So far, so good, I can access the extra parameters in my Front Controller (via $router->getParams();).

class FrontController{
  private $controller;
  private $view;

  public function __construct(Router $router){
    $route = $router->getRoute();
    ...
    $params = $router->getParams(); //Yay, I can get to the params here!
    ...
    $this->view = new View($route->getModel());
    ...
  }

Now here's where it gets complicated for me. This Front Controller constructs a View. I would like this View to also be able to access the Router's functions (e.g. to be able to get the URI parameters from it).

class View{
  public function output(){
    //But how do I access the Router's params here...?
  }
}

The first, simplest solution seems to be to make Router into a singleton or just make the function static and simply call Router::getParams()... But that's a no no because anti-patterns.

The second, obvious solution would be to pass my Router instance to the View's constructor. I want to avoid this in fear of my constructor becoming ginormous somewhere down the line. I'm not sure how many other classes I will need to access from the View like this and I don't want them to unnecessarily clutter its constructor. Is this fear justified?

Another solution would be to use a Service Locator and call something like $serviceLocator->getRouter() in my View. But that's, apparently, also an anti-pattern.

So what is the solution? Or is something just fundamentally wrong with my CMS' architecture?

Machavity
  • 28,730
  • 25
  • 78
  • 91
Daniel Frąk
  • 41
  • 1
  • 3

1 Answers1

2

FrontController uses Dependency Injection, which is considered the best way to handle this. Because you're passing the instance of the class in directly, you're not creating a global-esque problem inherent in Singletons.

The fear that your View constructor will get bloated is unfounded. If you need a dependency then you need to inject it somewhere. You don't have to do this within your constructor, tho. You could always make a function like

public function setRouter(Router $router) {
     $this->router = $router;
}

And inject it like that.

If your class itself really gets bloated then you need to refactor it into subclasses.

Community
  • 1
  • 1
Machavity
  • 28,730
  • 25
  • 78
  • 91
  • Is injecting the Router dependency through several levels of instantiation (first into the Front Controller, then into the View, then who knows where else I might need to...) not considered bad practice? And when do I know that it's time to refactor my class into subclasses? When is there too many dependencies? – Daniel Frąk May 30 '16 at 16:35
  • 1
    Not really. You're injecting the same class so it will be injected as amny times as you need. My rule of thumb is if a class exceeds 700 lines you need to refactor. As for too many dependencies... it depends on what you're doing – Machavity May 30 '16 at 20:53