告诉低级对象使用哪个工厂的好方法是什么?

时间:2018-10-05 16:12:40

标签: java dependency-injection factory-pattern

最近,我已经学习了很多有关设计模式的知识,特别是依赖注入。我很确定抽象工厂是实例化具有依赖关系的对象的好方法。但是我不确定如何告诉低级对象应该使用什么工厂。

考虑以下简化示例:

我有一个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。

我的问题是如何解决这个问题?谢谢。

1 个答案:

答案 0 :(得分:2)

在使用依赖注入时,抽象工厂模式经常被过度使用。这并不意味着它本身就是一个错误的模式,但是在许多情况下,对于“抽象工厂”模式有更多合适的替代方法。 Dependency Injection in .NET, second edition(第6.2章)对此进行了详细描述,其中:

    抽象工厂不应用于创建短暂的,有状态的依赖项,因为依赖项的使用者应忽略其生存期;从消费者的角度来看,从概念上讲,服务应该只有一个实例。
  • 抽象工厂经常违反依赖反转原理(DIP),因为它们的设计通常不适合消费者,而DIP则声明:“抽象归上层/策略层所有”,这意味着抽象的消费者应该以最适合其需求的方式来决定其形状并定义抽象。让消费者既依赖工厂依赖关系,又依赖于它所产生的依赖关系,使消费者变得复杂。

这意味着:

  • 应避免使用无参数create方法的工厂,因为这意味着依赖项是短暂的,并且其寿命由使用者控制。相反,应该为依赖关系创建抽象工厂,这些依赖关系从概念上讲需要创建运行时数据(由使用者提供)。
  • 但是即使在工厂方法包含参数的情况下,也必须注意确保确实需要抽象工厂。代理模式通常(但并非总是)更适合,因为它允许使用者具有单一依赖性,而不是同时依赖工厂及其产品。

依赖注入在应用程序的启动路径中促进了类的组合,本书将其称为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());
    }
}