工厂方法更适合框架,抽象工厂更适合库吗?

Is Factory method more suitable for frameworks and Abstract facory for Library?

抽象工厂模式和工厂方法模式都是创建型设计模式,解决了不同场景下的对象创建问题。

根据 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.

我的理解: Client 的动机是让基础工厂 class 中存在的方法得到执行,该方法依赖于具体 class 现在未知的对象(在这种情况要么在向客户提供软件的过程中定义,要么客户自己编写具体实现,最有可能的情况是Framework)。未知(或可能更改)的产品提供了一个抽象类型:IProduct,并设置了一个约定,将来任何产品的实现都必须实现此接口。

I产品界面

package com.companyx;
public interface IProduct {
    public void serve();
}

Factory class with 'a method' 需要执行

package com.companyx;
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
    }
}

一些具体的产品

package com.companyx;
class AppAProductFeatureX implements IProduct{
    @Override
    public void serve() {
        //some code
    }
}

具体产品工厂

package com.companyx;
public class AppAFeatureXProductFactory extends Factory{
    @Override
    public IProduct createProduct() {
        return new AppAProductFeatureX();
    }
}

客户代码

package com.clientcompany;
import com.companyx.AppAFeatureXProductFactory;
import com.companyx.Factory;
public class Client {
    public static void main(String[] args) {
        Factory fact = new AppAFeatureXProductFactory();
        fact.executeJob();
    }
}

根据 GOF 抽象工厂 模式

Provide an interface for creating families of related or dependent objects without specifying their concrete classes.

我的理解 client 对产品感兴趣,此模式通过将具体产品 classes 隐藏在工厂 classes 后面来帮助提供产品。

客户想要的产品类型

package com.companyb;
public interface IProductA {
    public void performAJob();
}

产品实施

package com.companyb;
//can be named better, but lets go with this name for this time
public class ProductAVersion1 implements IProductA{
    @Override
    public void performAJob() {
        // some code
    }
}

工厂接口,(也可以是抽象class)

package com.companyb;
public interface IFactory {
    public IProductA createProduct();
}

Factory o create ProductA的具体实现

包 com.companyb;

public class FactoryA implements IFactory{
    @Override
    public IProductA createProduct() {
        return new ProductAVersion1(); // concrete class of product is hidden
    }
}

客户代码

package com.clientcompany.productprovider;
import com.companyb.IFactory;
import com.companyb.IProductA;
public class SomeClientClass {
    private IFactory factory;
    private IProductA product;

    public void doSomeJobWithProductA() {
        // some code
        product.performAJob();
        //someCode();
    }
    public void setFactory(IFactory factory) {
        this.factory = factory;
        this.product = factory.createProduct();
    }
}

package com.clientcompany.productprovider;
import com.companyb.FactoryA;
public class SomeOtherClientCode {
    public static void main(String[] args) {
        SomeClientClass someClientClass = new SomeClientClass();
        someClientClass.setFactory(new FactoryA());
        someClientClass.doSomeJobWithProductA();
    }
}

Q1 : 抽象工厂模式中是否需要相关产品系列,如果只有一种产品(如上),该模式是否仍然相关各种子类型但不是各种相关类型?

Q2我上面的理解对吗?

Q3 上面给我带来了另一个疑问:工厂方法是否更适合框架(客户可以在框架中提供产品的实现),就像模板方法模式一样,工厂调用createProduct() 用户提供具体工厂实施形式的具体实施? 同样,抽象工厂更适合库开发,其中具体产品 classes(可能会有所不同)隐藏在更稳定的工厂 classes ?

后面

据我了解:

A1:产品系列不需要一定相关,如example diagram所示,只需要知道该类型的客户的产品。这种关系很自然,因为您可能不希望同一个客户端创建 2 个不相关的对象,例如,如果您有一个创建 Pizza 和 Cars 的 "AbstractPizzaFactory",那看起来会很奇怪,不是吗?

A2:从技术上讲,您可以在工厂模式中提供一个默认工厂方法,这样您仍然可以创建(默认)新对象而不必总是对其进行子类化。

A3:在这一点上我同意你的看法,尽管创建库或框架从来都不是非黑即白的。

我很难穿上你的鞋子。但是我对这个主题很感兴趣,所以我会尝试一下。这里涉及到libraryframeworkfactory methodabstract factoryproduct family等概念

首先,library vs framework 与工厂、抽象工厂或任何模式无关。库与框架的争论不是从模式发挥作用的实现角度出发的。例如,JUnit 是一个带有丰富断言库的框架。那么 junit 是否应该在其内部实现中更喜欢一种模式而不是其他模式? Java本身就是一个框架,自带一个JCL库。类似于java的Dot Net甚至称自己为框架,并包含BCL库。因此,在实现环境中不再有框架与库的争论。

所以问题归结为您应该使用哪种模式?这不是关于区别,这是清楚的,而是关于在什么场景下使用哪个。在这种情况下,Client Code 是所有可能 call 您正在输入的代码。某些代码是由您还是其他人在相同或不同的 jar 中、来自相同或不同的组织编写的并不重要。从一个代码片段的角度来看,任何其他代码(即使在相同class的不同方法中)是客户端代码如果该代码有可能 call 您正在输入的代码。

在键入任何代码(无论框架或库状态如何)时,有时我们需要获取某些对象的实例。可能我们需要 BufferedReader。如果在编译时我们绝对确定对象的具体 class,KISS 它并使用 newing.

我们可能不知道实例的具体class。现在问题是客户端代码是否有这个信息?如果客户端代码知道实际的具体 class,但我不知道,那么我们使用 FactoryMethod 模式。我正在输入的代码将在(比如说)它的参数列表中请求一个工厂对象的实例。知道实际具体 class 的客户端代码将提供一个工厂对象来进行创建。在 JDBC 中可以看到这种情况的示例,就像我们要处理 sql 语句一样。在编译时我们不知道我们应该实例化一个 mysql.JDBC4PreparedStatement 还是一个 microsoft.SQLServerStatement。它取决于连接字符串,这取决于最终用户。所以我们得到一个 Connection 实例并请求它到 createStatement()。看,我们将 sql.Statement 类型对象的构造委托给 sql.Connection 的子 class。这里的 conn 实例是工厂对象。我们如何获得工厂并不重要,只是我们从 客户端代码 .

获得它

如果我们需要一个 Process 对象的实例,并且在编译时我们不知道它是 Win32Process 还是 UnixProcess,我们将其责任委托给它创建 ProcessBuilder 与工厂模式相关 builder patternjdbc ConnectionManager.

也是如此

如果有许多不同的 classes 与继承无关但与 family 相关,我们可以使用 AbstractFactory。例如,jdbc 的点网对应 DbProviderFactory。我的代码需要 ConnectionCommandDataReader 等的实例,这些实例与继承无关,但与 MySql 的 family 或 SqlServer 相关.所以我们得到了 DbProviderFactory 的 subclass 的一个实例。可能是 MySqlProviderFactorySqlProviderFactory 取决于运行时和 客户端代码 。一旦我们有了那个工厂,我们就可以做 CreateCommand()CreateConnection()

希望这能帮助您在工厂模式和抽象工厂模式之间做出选择。

抽象工厂可以看作是工厂方法的集合。

为了更好地理解现实生活中的例子可以提供帮助:
Factory Method - plasticine/mold
Abstract Factory - 卡片工厂