Factory PatternDino BuljubasicBlockedUnblockFollowFollowingJun 16Every real world application will always consist of a number of classes each one responsible for some functionality.
Sooner or later, these classes will need to communicate with each other.
For example, class A needs to ask class B to do something by calling class B method.
Achieving this is easy by instantiating class B within class A.
To do so, we could for example have a field member of type B inside class A and then instantiate class B inside class A c-tor.
As an example, let’s consider Logger class and Processor class belowIf this level of complexity is all your application will ever have, then this is great but this also introduces coupling that will make your code more brittle as complexity grows.
Above, Processor has dependency on Logger and is therefore coupled with Logger.
Moreover, it hides dependency on Logger.
Look at the Processor’s API (public methods).
There is nothing in it’s API that reveals this dependency.
This means that Processor is not unit testable.
This is where Dependency Inversion Principle or D in SOLID comes to play.
Dependency inversion principle says “Higher level modules should always depend on abstractions rather than on implementations on lower level implementations”.
We should always keep in mind this when designing classes and make our classes depend on abstractions.
In C#, this means to make our classes depend on interfaces or abstract classes, rather than depending on concrete implementations of these.
Therefore our class Processor above should depend on some interface implemented by class Logger rather than on class Logger itself.
Let’s change thatThis is certainly a move in the right direction but we still have dependency on Logger class in our Processor.
So, we did not achieve anything even though our Processor is using interface ILogger as its member field.
The reason is that we are instantiating Logger inside Processor’s constructor (or anywhere else inside Processor).
Therefore, we have not removed this dependency at all.
Furthermore, the Processor’s API still does not reveal this dependency.
Since we cannot instantiate an interface, we are forced to still use the concrete implementation of Logger for this part; therefore, effectively contradicting everything we said so far.
Dependency injection is one way to resolve this and let the caller of Processor deal with instantiation of object and pass it to the Processor.
This “injecting” of dependency can happen through constructor, method, or property, depending on the needs and scope of dependency.
That way, Processor can receive any object implementing ILogger, mostly through constructor injection, and call it’s methods.
To do so, our Processor constructor changes so it can receive an ILoggerAnd caller can instantiate the real concrete Logger and pass it to ProcessorFactory pattern hides process of creating objects and abstracts them from the client.
The client knows nothing about object creation, all that is factory’s responsibility.
Client just receives the instance once it is created by the factory.
If we did not abstract this logic into factory, every class that needs to do logging would have to write this logic.
Typically, factory needs to receive some parameter to help it determine what it needs to create.
For example, above we may need a “console” logger, or a “file” logger, or some other logger.
So, our factory could receive a simple string or enumerated value specifying what it needs to instantiate.
Below we have two loggers, one writing to console and the other to write to a file.
Both implement ILogger interface so both are replaceable by ILoggerWe also have an enum specifying types of loggersAnd finally, our factory abstracting the creation logic away from clients is hereOur Processor now creates factory and let’s it deal with figuring out what and how to create it based on the input parameter passed to Process method.
In this case, it creates a Console logger type but we could alsoAs we can see, Processor neither depends on Logger implementation nor does it need to worry about its instantiation anymore as this is all abstracted into factory.
We can now also add more logger types without changing anything inside the client.
However, what about testing?.We now see that we need some LoggerType to pass into Process in order to get the logger, so we could conclude that this is no longer a problem assuming the API explains what logger types are valid.
Is the dependency removed?.Yes and no.
While dependency on Logger is removed, we introduced dependency on LoggerFactory.
But based on what we covered so far, this should be easy to refactor and remove this dependency.
Remember the Dependency Inversion principle above, we already covered it.
We just need to apply the same thinking here and create an abstraction of LoggerFactory.
This allows to easily swap the implementation if we need to and it also makes testing easier as we can now stub in a mock of ILoggerFactory instead of a concrete LoggerFactory in our unit tests.
Let’s do thatNow, our Processor can receive any factory implementing ILoggerFactoryNow, we don’t have any dependency neither on any of the loggers nor on any of the factories.
We can easily swap in new implementation of factory as long as it implements ILoggerFactory.
We can add more logger types without affecting client code (in this case Processor) and we can easily unit test our processor as it has no hidden dependency.
We easily see from its API that it needs an ILoggerFactory so we can mock it and pass the mock in for our unit tests and real logger for our production code.
So, we resolved pretty much all concerns except one.
Specifically, if we need to add a new logger type, we need to do some modification to our LoggerType enumeration and to our LoggerFactory as well.
Not a big deal but not that elegant as the case statement can quickly grow and become more complex.
This also violates O in SOLID or Open — Closed principle.
So, every time you see an if statement or a case statement, it is good to stop and think how can we refactor the code.
Let’s do exactly that.
Open-Closed principle states that entities should be open for extension but closed for modifications.
With that in mind, let’s refactor our LoggerFactory so that we don’t need to modify it if a new logger type is added.
Our Processor client now just receives ILoggerFactory as before but it does not need to pass it any parameter to let it know how to create object it needs as each factory creates only one type of loggerNo need for enumeration holding logger types and no need to change factory.
If we tomorrow decide to add a CloudLoggerFactory, all we need to do is create a new class for CloudLogger and for CloudFactoryLogger that implements ILoggerFactoryTherefore, we are no longer violating Open-Closed principle as both our Loggers and our Factories are both open to extensions and closed to modifications.
We also achieved Single Responsibility principle (or S in SOLID) as each loGger deals only with creating one particular type of the loggerThere is one small thing worth mentioning, well two actually.
First, we are passing ILoggerFactory into Processor’s constructor.
While this is perfectly valid, we could also pass it to Process method or to a new property.
All three options are valid and entirely depend on the scope of dependency.
If dependency is needed by the entire class, then pass it through the constructor.
If only one or more methods need it, pass it through method parameter.
Or if a property needs it, pass it to a property.
However, as projects grow, we might end up with lots of dependencies being passed around meaning we need to take care of when we create them and when we pass them.
This can get messy fairly quickly.
For that reason, using Inversion of Control containers (or IoC) such as Autofact or others is a better approach.
With these containers, you register your dependency types with the container and you never have to call new to create your dependent object and therefore you don’t need to pass it into the constructor either.
IoC container takes care of that for you and it also takes care of dependency scope.
In other words, the IoC container will new up your dependency, new up your class using the dependency.
It will also pass it the dependency and also take care of the scope of the dependency.
Hope you will find this article interesting and helpful.
We covered few concepts while discussing factory pattern, a very useful pattern that is helpful in keeping client code decoupled from dependent classes.
It also makes unit testing easier and allows for easy addition of new functionality without having to change existing concrete classes in your project.