告诉低级对象使用哪些工厂的好方法是什么?
What is a good way of telling low level objects which factories to use?
我最近学习了很多关于设计模式的知识,特别是依赖注入。我很确定抽象工厂是实例化具有依赖关系的对象的好方法。但是我不确定如何告诉较低级别的对象他们应该使用什么工厂。
考虑以下简化示例:
我有一个 class 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 似乎也不正确。
我的问题是如何解决这个问题?谢谢。
在使用依赖注入时,抽象工厂模式经常被过度使用。这并不意味着它本身就是一个糟糕的模式,但在许多情况下,抽象工厂模式有更合适的替代方案。这在 Dependency Injection Principles, Practices, and Patterns (paragraph 6.2) 中有详细描述,其中描述了:
- 不应该使用抽象工厂来创建短暂的、有状态的依赖项,因为依赖项的消费者应该忘记它的生命周期;从消费者的角度来看,在概念上应该只有一个服务实例。
- 抽象工厂通常违反依赖倒置原则 (DIP),因为它们的设计通常不适合消费者,而 DIP 声明:"the abstracts are owned by the upper/policy layers",这意味着抽象的消费者应该决定其形状和以最适合其需要的方式定义抽象。让消费者同时依赖工厂依赖和它产生的依赖会使消费者复杂化。
这意味着:
- 应避免使用无参数创建方法的抽象工厂,因为这意味着依赖关系是短暂的,并且其生命周期由消费者控制。相反,应该为概念上需要创建运行时数据(由消费者提供)的依赖项创建抽象工厂。
- 但即使工厂方法包含参数,也必须注意确保抽象工厂确实是必需的。代理模式通常(但不总是)更适合,因为它允许消费者有一个单一的依赖关系,而不是依赖于工厂和它的产品。
依赖注入在应用程序的启动路径中促进 classes 的组合,本书将这一概念称为 Composition Root。 Composition Root 是一个靠近该应用程序入口点(您的 Main
方法)的位置,它知道系统中的所有其他模块。
因为组合根依赖于系统中的所有其他模块,所以在组合根中使用抽象工厂通常没有什么意义。例如,如果您定义了一个 IXFactory
抽象来产生 IX
依赖关系,但是 Composition Root 是 IXFactory
抽象的唯一消费者,那么您正在解耦一些不需要解耦的东西: 组合根本质上以任何方式了解系统的所有其他部分。
您的 IGeneticAlgorithmFactory
抽象似乎就是这种情况。它的唯一消费者似乎是您的 Composition Root。如果这是真的,则可以简单地删除此抽象及其实现,并且可以简单地将其 getInstance
方法中的代码移至 MainProgram
class(用作您的组合根)。
我很难理解你的 IIndividual
实现是否需要工厂(自从我在大学实现遗传算法以来至少已经有 14 年了),但它们看起来更像是运行时数据而不是 'real' 依赖项。因此工厂在这里可能是有意义的,尽管要验证它们的创建和实现是否必须隐藏在抽象之后。我可以想象当 FastGeneticAlgorithm
直接创建 SmallIndividual
实例时,应用程序的耦合度足够低。然而,这只是一个大胆的猜测。
最重要的是,最佳实践是应用构造函数注入。这可以防止 Temporal Coupling. Furthermore, refrain specifying the implementations dependencies in the defined abstractions, as your AbstractGeneticAlgorithm
does. This makes the abstraction a 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());
}
}
我最近学习了很多关于设计模式的知识,特别是依赖注入。我很确定抽象工厂是实例化具有依赖关系的对象的好方法。但是我不确定如何告诉较低级别的对象他们应该使用什么工厂。
考虑以下简化示例:
我有一个 class 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 似乎也不正确。
我的问题是如何解决这个问题?谢谢。
在使用依赖注入时,抽象工厂模式经常被过度使用。这并不意味着它本身就是一个糟糕的模式,但在许多情况下,抽象工厂模式有更合适的替代方案。这在 Dependency Injection Principles, Practices, and Patterns (paragraph 6.2) 中有详细描述,其中描述了:
- 不应该使用抽象工厂来创建短暂的、有状态的依赖项,因为依赖项的消费者应该忘记它的生命周期;从消费者的角度来看,在概念上应该只有一个服务实例。
- 抽象工厂通常违反依赖倒置原则 (DIP),因为它们的设计通常不适合消费者,而 DIP 声明:"the abstracts are owned by the upper/policy layers",这意味着抽象的消费者应该决定其形状和以最适合其需要的方式定义抽象。让消费者同时依赖工厂依赖和它产生的依赖会使消费者复杂化。
这意味着:
- 应避免使用无参数创建方法的抽象工厂,因为这意味着依赖关系是短暂的,并且其生命周期由消费者控制。相反,应该为概念上需要创建运行时数据(由消费者提供)的依赖项创建抽象工厂。
- 但即使工厂方法包含参数,也必须注意确保抽象工厂确实是必需的。代理模式通常(但不总是)更适合,因为它允许消费者有一个单一的依赖关系,而不是依赖于工厂和它的产品。
依赖注入在应用程序的启动路径中促进 classes 的组合,本书将这一概念称为 Composition Root。 Composition Root 是一个靠近该应用程序入口点(您的 Main
方法)的位置,它知道系统中的所有其他模块。
因为组合根依赖于系统中的所有其他模块,所以在组合根中使用抽象工厂通常没有什么意义。例如,如果您定义了一个 IXFactory
抽象来产生 IX
依赖关系,但是 Composition Root 是 IXFactory
抽象的唯一消费者,那么您正在解耦一些不需要解耦的东西: 组合根本质上以任何方式了解系统的所有其他部分。
您的 IGeneticAlgorithmFactory
抽象似乎就是这种情况。它的唯一消费者似乎是您的 Composition Root。如果这是真的,则可以简单地删除此抽象及其实现,并且可以简单地将其 getInstance
方法中的代码移至 MainProgram
class(用作您的组合根)。
我很难理解你的 IIndividual
实现是否需要工厂(自从我在大学实现遗传算法以来至少已经有 14 年了),但它们看起来更像是运行时数据而不是 'real' 依赖项。因此工厂在这里可能是有意义的,尽管要验证它们的创建和实现是否必须隐藏在抽象之后。我可以想象当 FastGeneticAlgorithm
直接创建 SmallIndividual
实例时,应用程序的耦合度足够低。然而,这只是一个大胆的猜测。
最重要的是,最佳实践是应用构造函数注入。这可以防止 Temporal Coupling. Furthermore, refrain specifying the implementations dependencies in the defined abstractions, as your AbstractGeneticAlgorithm
does. This makes the abstraction a 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());
}
}