关于java:Factory方法更适合框架和抽象工厂库吗?

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

Abstract Factory和Factory方法模式都是创建设计模式,它解决了不同场景下的对象创建问题。

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

我的理解 :
客户的动机是获取基础工厂类中存在的方法得到执行,这取决于现在不知道具体类的对象(在这种情况下,无论是在向客户提供软件期间,它都将被定义,或者它将是客户自己编写的具体实现,很可能是在Framework的情况下)。未知(或可能更改)的产品提供了一个抽象类型:IProduct,并设置一个合同,以后在任何产品实现中都必须实现此接口。

IProduct接口

1
2
3
4
package com.companyx;
public interface IProduct {
    public void serve();
}

带有'a method'的工厂类需要执行

1
2
3
4
5
6
7
8
9
10
11
12
13
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
    }
}

一些具体的产品

1
2
3
4
5
6
7
package com.companyx;
class AppAProductFeatureX implements IProduct{
    @Override
    public void serve() {
        //some code
    }
}

工厂的混凝土产品

1
2
3
4
5
6
7
package com.companyx;
public class AppAFeatureXProductFactory extends Factory{
    @Override
    public IProduct createProduct() {
        return new AppAProductFeatureX();
    }
}

客户代码

1
2
3
4
5
6
7
8
9
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.

我的理解
客户对产品感兴趣,这种模式通过隐藏工厂类后面的具体产品类来帮助提供产品。

客户需要的产品类型

1
2
3
4
package com.companyb;
public interface IProductA {
    public void performAJob();
}

实施该产品

1
2
3
4
5
6
7
8
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
    }
}

工厂界面,(也可以是抽象类)

1
2
3
4
package com.companyb;
public interface IFactory {
    public IProductA createProduct();
}

Factory的具体实现o创建ProductA

package com.companyb;

1
2
3
4
5
6
public class FactoryA implements IFactory{
    @Override
    public IProductA createProduct() {
        return new ProductAVersion1(); // concrete class of product is hidden
    }
}

客户代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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 Above在我的脑海中带来了另一个疑问:Factory方法是否更适合于框架(客户端可以提供产品的实现),就像模板方法模式一样,工厂调用用户提供的具体工厂实现的createProduct()具体实现?
同样,抽象工厂更适合图书馆开发,具体的产品类别(可能会有所不同)隐藏在更稳定的工厂类别背后?


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

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

所以问题归结为你应该使用哪种模式?它不是关于差异,这是明确的,而是关于在哪种场景中使用哪一个。在此上下文中,Client Code是可能call您正在键入的所有代码。一些代码是由您或其他人在相同或不同的jar中,来自相同或不同的组织编写并不重要。从一个代码片段的角度来看,任何其他代码(即使在同一个类的不同方法中)也是客户端代码,如果该代码有可能call您正在键入的代码。

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

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

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

如果有许多不同的类通过继承而不是与家族相关,我们可以使用AbstractFactory。例如,取jdbc的点网对应DbProviderFactory。我的代码需要ConnectionCommandDataReader等实例,这些实例与继承无关,而是与MySql或SqlServer系列有关。所以我们得到了一个DbProviderFactory子类的实例。可能是依赖于运行时和客户端代码的MySqlProviderFactorySqlProviderFactory。一旦我们有了这个工厂,我们就可以做CreateCommand()CreateConnection()等。

希望这有助于您在工厂模式和抽象工厂模式之间进行选择。


在我的理解中:

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

A2:从技术上讲,您可以在Factory模式中提供默认的工厂方法,这样您仍然可以创建(默认)新对象,而无需始终对其进行子类化。

A3:我同意你的观点,尽管创建一个图书馆或一个框架绝不是黑白分明的。


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

为了更好地理解现实生活中的例子可以帮助:
工厂方法 - 橡皮泥/模具
抽象工厂 - 卡片厂