I am working on a C# programming project in Visual Studio. I have created various VS library projects inside the VS solution containing the various components of the solution. Without giving it too much thought, I have put the factories for these components into their corresponding VS projects.
I have now needed to reuse one of these components inside another VS solution and realized that having the factory inside the components VS project may not be the correct way, since in the new project I have different requirements for the factory to construct the object (e.g. parameters, etc.). So this would suggest, that the factory should not form part of the components VS project.
On the other hand factories appear to be very closely bound to the components/objects they construct. For me this raises the question, where I should put them:
Should factories be part of the solution using the components library or should it be part of the library?
Best Answer
TLDR
It depends on how configurable and extendable you want to have the reusable library (project), without touching it.
In the end you either have factories in the library itself, or the library does not have them and the client (the user) of the library is responsible for creating them and instantiating the library himself.
Longer answer
No matter how object oriented your code is, a part of OO code will always have to be procedural. The part where the object graph is constructed, the
new
ing of the classes.When dealing with construction of classes belonging to another project (this will usually be some common library which contains code useful throughout more projects), you usually have two options how to expose the project to the outer world.
For this demostration purpose I chose the cache layer, which, from my experience, usually has very similar API throghout many different projects, only the configuration is different.
1. Creating a public endpoint of the library to expose it
If you were to follow this ideology, all the classes inside your
cache
library project would be set tointernal
except one class, perhaps aYourNamespace.Library.Cache
class, which would be set topublic
, thus if you were to use the cache library, the only class you would have publicly access to is one single endpoint.The
YourNamespace.Library.Cache
class constructor could look like this:Not saying very much, is it? What exactly is the magic I am talking about?
Notice the
configuration
string as a parameter. It is a variable in which you define what you want to have, you insert your configuration, and this one public endpoint, theYourNamespace.Library.Cache
class, will, in its constructor, parse this string, extract the data it needs from it, and based on the user input construct everything necessary.Inside this constructor the
Cache
endpoint will instantiate the factories belonging to the cache project, it will the use those to instantiate the correct implementations.In your main project, the code then could be as simple as this:
By parsing the string, the constructor will then see, you want to cache to redis, and provided the configuration, it will use the configuration and pass it onto the internal classes of the cache library, which know what to do with it.
The placement of the factories
When using this approach, the factories are inside the cache project. In fact, the one public endpoint, the
Cache
class, acts as a factory itself.The project itself calls
new
on desired object based on theconfiguration
string and after the construction of the endpoint, should it not throw an exception, you are set to use all the functionality of the library.The good
The bad
2. Exposing everything inside the library and letting the client of the library decide what he wants to construct
Using this approach, (almost) all classes inside the cache library will be set to public, all the interfaces, all the implementations, everything.
Besides the unit tests part of the cache library, there will probably be no usage of the
new
keyword inside the cache library. The library itself will not construct anything, it will only provide constructors and methods with predefined types of parameters, to let you know which exact class you will need if you want to instantiate a class.The placement of the factories
Considering the cache library itself does not construct anything, the only place where you will have the factories is your main project, or the project of the client using the library.
The client himself will be responsible for constructing the object graph, by
new
ing the dependencies, until he creates the final object representing the cache itself.The good
The bad
You are likely to see something like this in the client code:
This is an extreme example, the amount of dependencies would probably vary, based on the type of cache you would like to construct, but it can happen.
Summary
Before diving in, you should decide, what it is you want.
If you want to encapsulate the library and want to have it its own environment and hide its complexity behind a simple abstraction layer, I would say go with a variation the first approach.
I would also use the first approach when I wanted to reflect all the internal changes in the library in all the projects where the library is used.
If you want the library to be easily extendable and you are likely to provide custom implementations, go with approach number two, where the library is not responsible for the construction, but gives clues on how the construction should happen.
But be aware, that change in the library will most likely need more change in the client code.
There is also always the third approach, which is a result of combining the first and second together, where then there are factories in both the library itself and also the client code.