最近,我已经学习了很多有关设计模式的知识,特别是依赖注入。我很确定抽象工厂是实例化具有依赖关系的对象的好方法。但是我不确定如何告诉低级对象应该使用什么工厂。
考虑以下简化示例:
我有一个MainProgram类(我刚刚做了这个,以表示我的程序中还有其他代码。) 在运行时的某个时刻,我想使用抽象工厂实例化IGeneticAlgorithm:
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();
}
//...
}
在运行时的某个时刻,我想在自己的GeneticAlgorithm中实例化一个IIndividual。 IIndividual不能在启动时实例化。能够在运行时实例化IIndividual的需求来自于遗传算法的工作方式,基本上,在选择,重组,变异的每个步骤之后,都必须实例化新个体。 (有关更多信息,请参见https://en.wikipedia.org/wiki/Genetic_algorithm)。为了使此示例简单,我选择在此处仅给AbstractGeneticAlgorithm一个IIndividual。
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{
//...
}
在FastGeneticAlgorithm中使SmallIndividualFactory成为静态变量对我来说似乎不是好的习惯。并将SmallIndividualFactory传递给Main,这样Main便无法将其传递给FastGeneticAlgorithm。
我的问题是如何解决这个问题?谢谢。
答案 0 :(得分:2)
在使用依赖注入时,抽象工厂模式经常被过度使用。这并不意味着它本身就是一个错误的模式,但是在许多情况下,对于“抽象工厂”模式有更多合适的替代方法。 Dependency Injection in .NET, second edition(第6.2章)对此进行了详细描述,其中:
这意味着:
依赖注入在应用程序的启动路径中促进了类的组合,本书将其称为Composition Root。 Composition Root是靠近该应用程序入口点(您的Main
方法)的位置,并且它知道系统中的所有其他模块。
因为合成根依赖于系统中的所有其他模块,所以在合成根中使用抽象工厂通常没有多大意义。例如,如果您定义了一个IXFactory
抽象来产生IX
依赖项,但是“合成根”是IXFactory
抽象的唯一使用者,那么您就可以将不需要去耦的事物去耦:“合成根”本质上以任何方式了解系统的每个其他部分。
您的IGeneticAlgorithmFactory
抽象似乎就是这种情况。它的唯一使用者似乎是您的成分根。如果是这样,则可以简单地删除此抽象及其实现,并且可以将其getInstance
方法中的代码简单地移至MainProgram
类(用作您的“合成根”)中。
对于我来说,很难理解您的IIndividual
实现是否需要工厂(自从我在大学实施遗传算法以来至少已有14年了),但它们看起来更像是运行时数据,而不是“真实”依赖。因此,尽管可以验证工厂的创建和实现是否必须隐藏在抽象的后面,但工厂在这里还是有意义的。我可以想象,当FastGeneticAlgorithm
直接创建SmallIndividual
实例时,应用程序之间的耦合就足够松散了。但是,这只是一个疯狂的猜测。
最重要的是,最佳实践是应用构造函数注入。这样可以防止Temporal Coupling。此外,请避免像AbstractGeneticAlgorithm
那样在定义的抽象中指定实现的依赖关系。这使抽象成为Leaky Abstraction(违反了DIP)。而是通过在实现上将它们声明为构造函数参数来声明依赖关系(在您的情况下为FastGeneticAlgorithm
)。
但是即使存在IIndividualFactory
,也可以按照以下最佳做法来简化代码:
// 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());
}
}