I don’t follow you on the difference between my request object and the dispatcher… Are they not exactly the same thing? From my understanding, they at least seem very similar, creating controllers OR ‘dispatching’ to an external url?
I’m just trying to understand how your system is working. They may well be the same.
You say that the dispatcher could potentially be called from anywhere in the system, isn’t that against dependency injection practice/global namespace?
Well anywhere that needs it. How it’s passed around is a separate issue but for example it’s useful for a view to initiate an MVC triad. The dispatcher gives a common interface to the initialisation logic so it can be re-used for example from a view, the URL router or anything else which might need it.
And how would the dispatcher be the only thing that knows about controllerFactory (Should you set up the factory within the dispatcher, instead of passing it in?)?
That was perhaps a little conservative, but the controllerFactory itself probably has no reason to be passed around the system other than to the dispatcher from your boostrap code/entry point.
I thought it may be useful to use the request object to hold information related to the request which may or may not be of interest to the controller, or be useful when doing a sub-request (e.g. external url). I may be doing something really stupid by doing this. I just found it reasonable to do it like this…
Generally, in OOP if you’re describing what a class is for and use the word “And”, it’s doing too much and should be split.
If you split your request object into a dispatcher and details about the request (get/post, etc) does this solve the problem?
I’m not sure it would, however, because the controller may still need to call the dispatcher. What I still can’t understand is why your request object requires a reference to controllers it’s created. Wouldn’t removing this reference fix the circular referencing issue?
The highlighted part is exactly what I thought, hence why I thought a factory should be involved in creation of singeltons as well. It just feels strange to use factories everywhere and don’t do it with singeltons just because they are singeltons (which shouldn’t be any different from regular objects in regards to needing creation logic or different requirements in the future). I guess it comes down to personal preference, but I don’t think I would mind about the extremely small overhead by having a simple factory for singeltons as well just to increase consistency.
If Michael hasn’t been annoyed out of the topic by lamp, I’m sure he’ll come in and argue against this. And I’d agree with him.
The application has to be initialized somewhere.
This code is entirely redundant:
$factory = new Factory($someDependency);
$foo = $factory->create('foo');
it can be replaced with:
$foo = new Foo($someDependency);
If you’re creating a factory, using it within the same scope and never using it again, there is zero benefit to its use.
As you say, the only potential argument is consistency. But if you extrapolate from that, you need a factory to create your factories and a factory to create that. ad infinitum.
The factory pattern doesn’t exist to eliminate the new keyword. It exists to allow client code to work without worrying about correctly initilising the (correct type of) object. Since the top level of code’s sole job is object initialisation and set up I’d argue that a factory is redundant here.
Of course it won’t harm the system per se (it will introduce additional complexity and a negligible performance hit though) but won’t add any benefit either.
The real answer is a DIC. Let the DIC create the singletons when something that needs them is created.
If not using a DIC, all the dependencies these objects should need are created at the same top level and are available in current scope.
I just want to add a quick disclaimer to all of this theory:
As we’ve seen, it’s possible to attempt to find the best solution and spend a lot of time doing so. Firstly, there’s no one-size fits all approach and generally no ‘correct’ answer, only best practices. Secondly, and more importantly, spending a long time over-analysing small implementation details like these can be detrimental to the timely completion of the project