什么时候应该使用工厂方法模式? (而不是组成)

When should we use the factory method pattern? (instead of composition)

根据 GOF 书,Factory method pattern

Define an interface for creating an object, but let the subclasses decide which class to instantiate. Factory method lets a class defer instantiation to subclass.

形态结构

public abstract class Factory {
    public abstract IProduct createProduct();
    private void performCriticalJob(){
        IProduct product = createProduct();
        product.serve();
    }
    public void executeJob(){
        //some code
        performCriticalJob();
        //some more code
    }
}


public interface IProduct {
    public void serve();
}
  1. 工厂需要一个对象(其具体 class 未知或具体 class 可能会根据不同的应用程序类型而改变)来执行任务。

  2. 由于不知道要实例化哪个class,所以根据需要的对象类型设置了一个标准的契约,这个契约放在接口中。

  3. 基工厂class 声明一个abstract 方法到return 上面定义的interface 类型的对象。 它让 subclasses 决定并提供对象创建的实现。

  4. 为了完成任务,它需要一个对象,只需调用 abstract 方法即可获取该对象。

问题优先考虑组合而不是继承。 上面的工厂方法使用继承来获取具体的产品。此外,subclasses 需要实现将创建的 createProduct 和 return ConcreteProduct。如果从中删除了抽象方法(这使得工厂 class 成为非抽象 class),而不是 Subclassing 工厂 class。现在 Factory class 可以由新的 classes 组成,具体的产品对象可以注入其中,如下例所示。

为了实现工厂方法模式定义的场景中的意图,为什么不使用正常的多态性,如下所示?我知道工厂方法模式还有更多我所缺少的东西,但是通过最喜欢的组合而不是继承,我找到了下面的方法来解决相同场景中的相同问题,这是 Factory method 中更好的方法。 Factory method 比下面的方法有什么优势?

public abstract class PolymorphismWay {
    private void performCriticalJob(IProduct product){
        product.serve();
        //some code
    }
    public void executeJob(IProduct product){
        //some code
        performCriticalJob(product);
        //some more code
    }
}

现在用户可以直接提供具体的对象[=70=,而不是要求用户通过实现createProduct方法来创建子工厂classes和return产品的具体对象]es 将 IProduct 实施为 executeJob.

[EDIT] 感谢您的回答和评论,但我也有同样的想法,这也带来了一些困惑。我研究了GOF工厂方法模式。它使用了一个 用于创建各种类型文档的应用程序框架 的示例。我的问题是学习后产生的疑惑。

网站和博客仅基于作者对模式的理解的反映,他/她可能已经阅读也可能没有阅读,理解模式的实际意图。理解 classes 不是主要的 objective。应该研究设计模式,考虑什么场景和什么问题是遵循良好的 OOP 原则的最佳解决方案(或者最少违反它们并破坏它们并有充分的理由这样做)。最佳解决方案是任何 design pattern 所解释的解决方案。 GOF 是一本标准的书,解释得很好。但是仍然有一些差距或疑问,这是这个问题的主要原因。

再读一遍您提供的定义:

Define an interface for creating an object, but let the subclasses decide which class to instantiate. Factory method lets a class defer instantiation to subclass.

这与您的第一个陈述有点矛盾:

Factory needs an object (whose concrete class is not known or whose concrete class may change as per the different application type ) to perform a task

工厂的目的是创建 对象,而不是执行任务

事实上,如果它要执行一项任务,它只是为了能够为您创建对象。 从工厂获得对象后,您可以执行关键工作。

关于优先于继承的组合,工厂方法也可以组合对象并返回一个对象组合。

工厂模式有很多很好的例子——例如 Wikipedia and blackwasp

编辑 - 关于 GoF Application 示例

Application 的 GoF 示例使用 Template Method 作为 Factory MethodApplication 定义工厂方法和围绕它的一些操作,但是 Application 的子 类 决定创建哪个 Document

你已经建议了,不要使用工厂方法。相反,在其他地方创建 Document 并在 Application 中创建 "injected"(a.k.a 依赖注入)。 您没有描述 Document 将在哪里创建(它仍然可以是工厂)。
现在 Application sub-类 对 Document 的创建没有任何控制。这完全改变了系统的行为和设计。

不代表好坏,只是方法不同而已。
在现实生活场景中,您必须仔细检查手头的问题并决定哪种设计最适合。

I know factory method pattern has something more which I am missing but going by the favouting composition over inheritance, i find the below way ,to solve the same problem in same scenario

使用 Factory Method 模式而不是问题中显示的普通旧组合时,您可以获得几个优势:

1.关注点分离和开闭原则 :您为每个相关的对象组创建一个工厂子class。该工厂 subclass 只负责创建属于特定组的产品。 ABCProductFactory 将只关心创建 ABCProduct1ABCProduct2 等。CDEProductFactory 将只关心创建 CDEProduct1CDEProduct2 等.对于每个新产品组,您创建一个新的子class,而不是修改现有的class。如果您采用组合方法,其他 class 将负责创建产品并将其传递给您的 Factory。随着你的产品种类增加到 ZZZProduct1ZZZProduct2 等等,这个 class 很快就会膨胀到一个巨大的规模,有太多的 if-else 条件来检查哪个产品子 class 创建。您最终会意识到这一点并定义一个 class 来创建每个相关的产品组。

2。产品创建和产品处理有一个契约: 在这种情况下,工厂方法模式与 template-method 模式非常相似,因为它为需要的操作指定了一个模板在创建对象后对其执行。这使您可以确保产品一旦创建,它将始终经历与工厂创建的任何其他产品相同的步骤。将此与您问题中的 Composition 示例进行比较,在该示例中,IProduct 在创建后应执行哪些步骤没有固定合同。我可以用一个名为 performCriticalJobpublic 方法创建一个名为 Factory2 的 class。没有什么能迫使我在 Factory2 中使用 executeJob 方法。即使我将 executeJob 方法添加到 Factory2,也没有什么强制我在 executeJob 中调用 performCriticalJob。您可以使用 模板 模式解决此问题。

现在应该清楚 Factory Method 模式基本上将对象创建和对象处理绑定在一个 class 中。对于您的组合示例,您将有很多移动的部分,但没有人控制它们应该如何协同工作。

底线: 在您的情况下,当您希望对象创建和对象处理具有固定契约时,请使用 Factory Method 模式这样所有对象在创建后都会经过相同的处理。使用您的组合示例,其中在创建对象后不需要遵循步骤的合同。

您的问题是,您坚持认为您的 Factory class 只有一种客户总是通过扩展它(代码 #1)或使用 class将新创建的 IProduct 传递给它的方法(代码#2)。这种客户端的全部目的是使 Factory 能够接收新创建的 IProduct。

不关心以上所有事情的普通客户怎么样!这些甚至不关心 class 是否是工厂。因此,他们不想要像您的代码 #2 中那样需要 IProduct 的方法。

确实,您应该将 Factory class 重命名为其他名称(例如 XXX),class 不是 "factory"!但是它的一些方法是"factory"。你看,模式名称是 "Factory Method",而不是 "Factory" 或 "Factory Object"。相反,在抽象工厂模式中,抽象工厂实际上是一个工厂对象。

P/S: 正确的组合方法是将抽象工厂传递给 XXX 的构造函数。

因为我们知道工厂方法模式依赖于继承,如果我们从没有工厂方法的部分开始并简单地问,我们什么时候应该支持继承而不是组合?

我们已经知道这个问题的答案是,“不经常”,因为 GoF 告诉我们总体上支持组合。然而,在现实世界中存在继承的场景。动物王国可能是典型的例子。当继承实际发生在一个域中时,在我们的代码中以相同的方式对其建模是有意义的。这也是我们应该考虑工厂方法模式的时候。仅当您已经确定继承适合上下文时才考虑它。先选择传承,再选择创造模式。不要引入 Factory Method 然后被迫进入否则不适用的继承关系。

一个更自以为是的答案是:永远不要在现代代码中使用工厂方法模式。坚持组合关系,所有这些关系都连接在一个依赖注入容器中。 GoF 模式不是建议。它们是 1980 年代设计的集合。类似于 Singleton 由于其负面影响而现在被认为比模式更反模式的方式,工厂方法在现代代码中的适用性非常小。在极少数情况下它可能是合适的,您会知道它,因为您已经在使用继承。