In almost all projects in which I participated at some point there was a need for a factory implementation.
There is already a pattern concept about factory, but it is not always the best solution. The solution proposed below is a particular approach (general and flexible) of the pattern concept.
The purpose of this solution was not to implement the original pattern, but to give an alternative implementation based on some of the pattern's exposed principles.
With this current factory implementation the idea is to have a base class and derived classes so that at some point to be able to get the instance of a derived class based on an identifier (not the class type, a label).
To exemplify such a need here is a small example: we have a base class called ImageBase and the derived classes called JPEG, GIF, etc. We have the image stored somewhere and we need to load it based on a label/identifier. It’s identifier comes from a different source (database, config file..). A quick solution is to make a method that will return an instance of the ImageBase class based on a parameter:
This seems to be a good and apparently quick solution, but when we want to support new image types this method must be modified in order to support/include the new types.
The solution I propose is based on labeling implementation classes with a custom Attribute for specifying implementation type (JPEG, GIF). When the factory is called it will receive a label and will return the instance of the corresponding implementation (marked with the label given as parameter).Some observations about this solution:
- Each implementation will add the FactoryLabel attribute which will specify the type that is implemented and derived from the base class.
- CreateImplementation method returns an instance of the implementation matching the label parameter.
- GetImplementationType method returns the type of the implementation which matches the label parameter
- When CreateImplementation or GetImplementationType is called first time a dictionary of Label – Type will be built. This dictionary acts like a cache for further requests.
- Implementation classes can be present in different assemblies. In this case a list of containing assemblies can be passed to factory. This list of assemblies is used to search for implementations. If no assembly is pointed, executing assembly is used to search for implementations.
- RunTypeHandler is used instead of Type, because for caching purpose is more lightweight than the Type.
- FactoryLabelAttribute takes an object for implementation identification. Practically this should be a value type (int, double, enum).
Bellow you can find some samples of this approach:
This might not be the perfect solution but is a point of start in creating a generic factory adapted to your needs.