I've been learning a lot about Design Patterns lately, specifically Dependency Injection. I'm pretty sure that abstract factorys are a good way of instantiating objects that have dependencies. However I'm not sure how to tell lower level objects what factories they are supposed to use.
Consider following simplified example:
I have a class MainProgram (I just made this to represent that there is other code in my program..) At some point during runtime I want to instantiate a IGeneticAlgorithm with an abstract factory:
public class MainProgram{
private AbstractGeneticAlgorithm geneticAlgorithm;
private IGeneticAlgorithmFactory geneticAlgorithmFactory;
public MainProgram(IGeneticAlgorithmFactory geneticAlgorithmFactory){
this.geneticAlgorithmFactory = geneticAlgorithmFactory;
}
private void makeGeneticAlgorithm(){
geneticAlgorithm = geneticAlgorithmFactory.getInstance();
}
public static void main(String[] args){
MainProgram mainProgramm = new MainProgram(new FastGeneticAlgorithmFactory());
//...
}
}
public interface IGeneticAlgorithmFactory{
public IGeneticAlgorithm getInstance();
}
public class FastGeneticAlgorithmFactory implements IGeneticAlgorithmFactory{
public IGeneticAlgorithm getInstance(){
return new FastGeneticAlgorithm();
}
}
public abstract class AbstractGeneticAlgorithm{
private IIndividual individual;
private IIndividualFactory individualFactory;
private void makeIndividual(){
individual = individualFactory.getInstance();
}
//...
}
At some point during runtime I want to instantiate an IIndividual in my GeneticAlgorithm. The IIndividual can't be instantiated at startup. The need to be able to instantiate the IIndividual during runtime comes from the way Genetic Algorithms work, where basically after each Step of Selection-Recombination-Mutation new Individuals have to be instantiated. (For more information see https://en.wikipedia.org/wiki/Genetic_algorithm). I chose to give the AbstractGeneticAlgorithm here only one IIndividual to keep this example simple.
public class FastGeneticAlgorithm implements AbstractGeneticAlgorithm{
private IIndividual individual;
private IIndividualFactory individualFactory;
}
public interface IIndividualFactory{
public IIndividual getInstance();
}
public class SmallIndividualFactory implements IIndividualFactory{
public IIndividual getInstance(){
return new SmallIndividual();
}
//...
}
public interface IIndividual{
//...
}
public class SmallIndividual implements IIndividual{
//...
}
Making the SmallIndividualFactory a static variable in the FastGeneticAlgorithm doesn't seem to me like good practice. And passing the SmallIndividualFactory to Main, so that Main can pass it down to FastGeneticAlgorithm also doesn't seem right.
My question is how to solve this? Thank you.
When it comes to using Dependency Injection, the Abstract Factory pattern is often over-used. This doesn't mean that it's a bad pattern per se, but in many cases there are more suitable alternatives for the Abstract Factory pattern. This is described in detail in Dependency Injection Principles, Practices, and Patterns (paragraph 6.2) where is described that:
This means that:
Dependency Injection promotes composition of classes in the start-up path of the application, a concept the book refers to as the Composition Root. The Composition Root is a location close to that application's entry point (your Main
method) and it knows about every other module in the system.
Because the Composition Root takes a dependency on all other modules in the system, it typically makes little sense consume Abstract Factories within the Composition Root. For instance, in case you defined an IXFactory
abstraction to produce IX
dependencies, but the Composition Root is the sole consumer of the IXFactory
abstraction, you are decoupling something that doesn't require decoupling: The Composition Root intrinsically knows about every other part of the system any way.
This seems to be the case with your IGeneticAlgorithmFactory
abstraction. Its sole consumer seems to be your Composition Root. If this is true, this abstraction and its implementation can simply be removed and the code within its getInstance
method can simply be moved into the MainProgram
class (which functions as your Composition Root).
It's hard for me to understand whether or not your IIndividual
implementations require a factory (it has been at least 14 years ago since I implemented a genetic algorithm at the University), but they seem more like runtime data rather than 'real' dependencies. So a factory might make sense here, although do verify whether their creation and implementation must be hidden behind an abstraction. I could imagine the application to be sufficiently loosely coupled when the FastGeneticAlgorithm
creates SmallIndividual
instances directly. This, however, is just a wild guess.
On top of that, best practice is to apply Constructor Injection. This prevents Temporal Coupling. Furthermore, refrain specifying the implementations dependencies in the defined abstractions, as your AbstractGeneticAlgorithm
does. This makes the abstraction a Leaky Abstraction (which is a DIP violation). Instead, declare the dependencies by declaring them as constructor arguments on the implementation (FastGeneticAlgorithm
in your case).
But even with the existence of the IIndividualFactory
, your code can be simplified by following best practices as follows:
// Use interfaces rather than base classes. Prefer Composition over Inheritance.
public interface IGeneticAlgorithm { ... }
public interface IIndividual { ... }
public interface IIndividualFactory {
public IIndividual getInstance();
}
// Implementations
public class FastGeneticAlgorithm implements IGeneticAlgorithm {
private IIndividualFactory individualFactory;
// Use constructor injection to declare the implementation's dependencies
public FastGeneticAlgorithm(IIndividualFactory individualFactory) {
this.individualFactory = individualFactory;
}
}
public class SmallIndividual implements IIndividual { }
public class SmallIndividualFactory implements IIndividualFactory {
public IIndividual getInstance() {
return new SmallIndividual();
}
}
public static class Program {
public static void main(String[] args){
AbstractGeneticAlgorithm algoritm = CreateAlgorithm();
algoritm.makeIndividual();
}
private AbstractGeneticAlgorithm CreateAlgorithm() {
// Build complete object graph inside the Composition Root
return new FastGeneticAlgorithm(new SmallIndividualFactory());
}
}