I'm really new to DI, but I really want to try using it.
There's something I don't understand. Here's a simple pseudocode of a factory, I'm using a lot.
class PageFactory {
public function __construct(/* dependency list */) {
... //save reference to the dependencies
}
public function createPage($pagename) {
switch ($pagename) {
case HomePage::name:
return new HomePage(/* dependency list */);
case ContactPage::name:
return new ContactPage(/* dependency list */);
...
default:
return null;
}
}
}
It has a really simple logic, it chooses the implementation instance based on a string. It's very useful, because I can choose at a later time what page I need, and only that one will be created.
How would I rewrite this code, so my page instances would be created by a dependency container, thus I wouldn't need to handle dependencies for the factory and the pages it creates?
The only solution I see is to make the container I want to use, a dependency for the factory, and make calls to it from within the factory. I have lot's of problem with that.
First, I don't want to couple the container into my application, and every factory it has.
Second, and my biggest problem is, that the call for the container is really messy, it's stringly typed (i.e. $container->get('Foo');). I would like to use it as few times as possible. Only once if possible.
EDIT:
I do not want to write a DI container. I want to use an existing one. My question is on usage. How would I use a DI container instead or within the above factory, while keeping the logic on instance selection.
EDIT 2:
I started using Dice as the DI container, because it's lightweight, and knows everything I need. I would prefer if I could use it in a single place and build up the whole application. For that, I'll need a way to get rid of these factories somehow, or modify it in some way to make those pages behave like dependencies, so the DI container would provide instances to them.
EDIT 3:
Yes, I need this for testing purposes. I'm also new to testing, but it is very awesome so far, I really like it.
These pages are what MVC frameworks call controllers. But all the MVC frameworks I checked out are not making their controllers testable, because they create instances of them automatically. And because they are created by the system, their constructor parameters are not customizable by the user.
There is a simple way to check this for any framework. I just look up the way I should use a database in a controller in that specific framework. Most frameworks are either procedural, or use some service locator, either way, they are getting their dependencies from public scope, something I do not want to do. This is why I'm not automating the controller instantiation. The downside is that I now have these weird factories, which carry a lot of dependencies. And I would like to substitute this task to a DI container.
Most frameworks implement their own testing mechanism, which are more like functional testing, instead of unit testing, but I do not want to do that either.
I know, it's an old question. But I'm currently looking for an answer to a similar, but more general question: how to correctly implement DI pattern in factories which decide what and how they create directly in runtime? Maybe my answer can help someone, who'll find this question in search engine (like I did)? And, maybe, it'll also be useful to you? Or, you'll share some of your experience that you've gained during these almost two years passed… — I hope, you're already not so “new to DI” as you were when you were asking this question on SO :) (I'm currently new to DI)
I found that this is a common question, but there isn't a common answer to it, especially in PHP. For example, here is how this problem is supposed to be solved in Guice (popular Java framework from Google, supporting DI):
Someone proposes to „new-up“ objects (create using the “new” operator) directly in such factories, that's OK they say. E. g. Miško Hevery, one of two original developers of AngularJS — popular JS framework from Google, — in his article «To “new” or not to “new”…» introduces his own separation principle: he says, it's OK to create “value objects” whenever and whereever you need them, whereas “service objects” can only be injected via DIC and are not allowed to be created directly.
But I personally disagree with them, because such factories can have some business logic, that makes it impossible to consider them a part of composition root of app (where newing-up is only allowable).
The solution: inject trivial factories into factories
IMO, the only solution that follows the DI pattern is to create special trivial “injection friendly” factories that depend on injector and return objects that they obtain directly from callin injector's methods. Sinse direct access to injector, like direct newing-up of own dependencies, is allowable only in composition root, therefore, the declaration of all these providers should be done in composition root. I'll demostrate my suggestion with the following example.
You wrote that you were going to use PHP-DI as DIC. Me too, I decided to use it in my projects, therefore, the examples below will also use it.
In the example above, when I wired up trivial factories to DI container, I, among other things, declared the interfaces of these factories and implemented them with inline anonimous classes instances (this feature was introduced in PHP 7). If you don't wanna bother yourself to write such interfaces, you may skip this and write these factories directly, without interfaces. The simplified example is listed below. Note that I omit steps 1, 2 and 4 in the example: step #1 gets removed because we no longer need to define those trivial interfaces, and steps 2 and 4 remain unchanged, except that I remove type hints from PageFactory constructor, referencing already non-existent interfaces. The only step that changed is the 3-rd step, which is listed below:
And finally, if you think it's OK to new-up objects in app's composition root (and that's probably really OK), you also may do it in these trivial factories instead of injecting the injector and creating instances using injector. But in such a case, you'll also have to manually instantiate all the dependencies of HomePage (or other page), which is OK if there are no such dependencies, but is undesirable if there are many of them. IMO it's better inject the injector and create object using it: this allows to manually specify only our trivial factories — and not other dependencies.
So, @SinistraD, what do you think about this suggested approach?
EDITED
After using the DI container for a few days I realized how simple the solution actually is, that I am now really embarrassed. It also helped that bad_boy recommended routing.
DI as router output handler
I can use the DI container to handle the output of a simple router. The problem with routers is that they return a class name to the framework, thus it's up to the framework to instantiate them. This is a problem, because then the constructor would be predefined (or simply empty) and dependencies would only come from public scope, or a service locator.
But in the case of a DI container the pages are already made by a framework, and not the user. So the solution is simply to allow for such a routing to exist, but then let the DI framework handle the output.
So it would look something like this:
This way I'm using the DI in a single place, in the root of my application, and I can have many routers containing any logic and dependencies, with any number of pages with any dependencies.
::class constants
I also had a great problem with these. Mainly, that they are PHP 5.5. I solved it by writing a small PHP preprocessor, that takes a PHP file, changes every ClassName::class to "ClassName", saves it to a special location not visible by my IDE, and I've set up my autoloader to only load the processed PHP files. And now, I can use ::class constants in my PHP 5.3 setup, by just adding a special extension to a PHP file, before the .php.
A properly implemented DI container is basically a "smart factory". But implementing one will probably be way beyond your current capabilities. It's kinda really complicated, because a good DI container will build the whole dependency tree.
For example:
Instead of making one yourself, you should go with already existing DI container.
My recommendation would be Auryn.
I think you are in a few ways missing the point of DI & DIC, one of the main points is to pass the container when creating instances.
Yeah, it uses strings as identifiers. Thats the whole point, it avoids tight coupling. And the signature for creating instances becomes much simpler.
If your want your IDE to be able to decode whats behind the property you can use docblocks
So how would you use that for a factory?
Simple enough, then in the Notification class you need a translator:
The whole point here is that you can hotswap any component anywhere along the chain which makes testing much easier.
You other issue when creating controllers seems to be that you are going about it wrong.
If your controllers all have the same creator args:
Then you can have a simple request matcher which pares together requests and controllers/actions.
I suggest you have a look at Symfony2 which has testable controllers and a router which is pretty easy to grasp and use as stand alone component and also Fabian Potenciers excellent article What is Dependency Injection?