I’ve finally decided to start blogging again after a not-so-brief interruption. I wanted to start slowly, from something I’m fairly comfortable with: Design Patterns.
But I don’t want to blog about eye candies, unicorns and rainbows.
I’d like to showcase things that you might encounter in the real world, on real applications.
So for this first article, we’ll see what a Factory is and an interesting twist that will make it Dependency Injection - friendly.
The Factory Pattern is one of the Creational patterns explained in the amazing Gang-of-Four book that every software engineer should have next to their bed. Quoting:
“Define an interface for creating an object, but let subclasses decide which class to instantiate. The Factory method lets a class defer instantiation it uses to subclasses.”
Let’s see a simple example in action:
We basically define a common interface,
IShapeRenderer and defer to our
IShapeRendererFactory implementation the burden of providing the right instance based on some input parameter.
Plain and easy.
Now, what’s the problem with this? Dependencies. This Factory assumes that every
IShapeRenderer implementation takes no dependencies and has a very simple instantiation mechanism.
If this is your case, then you are free to stop reading here.
One option to solve this is to simply inject all the dependencies we need in the Factory itself:
Now, this works fine and potentially solves all your problems. If the number of dependencies is low, I would call it a day. Although to be fair, I’m not a real fan of this approach. Code tends to evolve organically, especially when multiple devs are working on a project. Moreover, you might incur into some issues with the different lifetimes of your dependencies. And trust me, you don’t want to deal with that.
One possible solution is to inject individual mini-factories into the main Factory and use a quick dictionary lookup to find and consume the right one:
It’s all about flexibility.
This approach is simple to write, fits nicely into the DI Container and allows using any kind of lifetime you want for each individual implementation.