为什么我们需要抽象工厂设计模式?


124

大多数定义说:

抽象工厂提供了用于创建相关对象族的接口,而无需指定其具体类

抽象工厂模式的用途是什么,因为我们可以通过创建具体类本身的对象来完成任务。为什么我们有一个工厂方法来创建Concrete类的对象?

请提供任何我必须实现abstractFactory模式的现实示例?

Answers:


219

6
所有这些示例都描述了工厂方法模式,因为它们都返回单个产品接口。这些都不是抽象工厂模式,因为它们都不产生一系列相关的产品接口。
jaco0646 '16

32
为了充分公开,该答案的作者应明确指出他也是每个链接的答案的作者;因此,此列表并非来自SO社区的代表性示例。
jaco0646 '16

1
@ jaco0646 IIRC,“ 工厂方法”模式是“ 模板方法”模式的一种特殊化,它依赖于继承。不过,由于我当前正在旅行,并且可能没有我的GoF书,所以我可能会误会。“没有一个产生一系列相关的产品接口”是什么意思?
Mark Seemann

1
指示工厂不符合抽象工厂模式的最简单线索是计算工厂生产的抽象产品(我用术语“产品接口”代替“抽象产品”,以避免过度使用“抽象”一词) 。生产单个抽象产品的工厂不能是抽象工厂,因为根据定义,抽象工厂生产一系列相关产品。至关重要的是要注意,该系列并不是指一个接口的不同实现,而是指具有不同,相关接口的产品。
jaco0646 '16

1
这是oodesign.com/abstract-factory-pattern.html的示例。这就是最初为抽象工厂模式创建的。
user2802557 '16

23

使用Abstract Factory模式的一个现实示例是提供对两个不同数据源的数据访问。假设您的应用程序支持不同的数据存储。(例如,SQL数据库和XML文件)。您有两个不同的数据访问接口,例如IReadableStore和和,它们IWritableStore定义了应用程序期望的通用方法,而与所使用的数据源的类型无关。

应该使用哪种类型的数据源不应更改客户端代码检索其数据访问类的方式。您AbstractDataAccessFactory知道配置了哪种类型的数据源,并为客户端代码(即或)提供了具体的Factory。这些具体工厂可以创建具体的实现,例如和。SqlDataAccessFactoryXmlDataAccessFactorySqlReadableStoreSqlWriteableStore

.NET Framework中的DbProviderFactory是此模式的示例。


18
该答案可以准确地描述工厂方法模式或静态工厂模式,但不能描述抽象工厂模式。
jaco0646 '16

5

如果我对您没错-问题是,为什么我们同时具有Factory方法和抽象工厂模式。当不同的多态类具有不同的实例化过程时,您需要抽象工厂。而且,您希望某个模块创建实例并使用它们,而又不知道对象初始化的任何细节。例如-您想要创建进行一些计算的Java对象。但是其中一些是应用程序的一部分,而其他的字节码则应该从数据库中读取。另一方面-为什么我们需要工厂方法?同意,那个抽象工厂与它重叠。但是在某些情况下-编写的代码要少得多,具有更少的类和接口使系统更易于理解。


3

抽象工厂模式的用途是什么,因为我们可以通过创建具体类本身的对象来完成任务。为什么我们有一个工厂方法来创建Concrete类的对象?

在没有Abstract Factory的情况下,客户需要了解具体类的详细信息。这种紧密的耦合已由Abstract Factory删除。

现在,工厂方法公开了客户必须使用的合同。您可以通过添加实现工厂方法公开的接口的新产品来向工厂添加更多产品。

请参阅以下相关的SE问题,以更好地理解:

工厂模式和抽象工厂模式之间的基本区别是什么?

意图:

提供一个用于创建相关或相关对象族的接口,而无需指定其具体类。

你能理解意图,结构,清单和经验法则抽象工厂从这种模式sourcemaking文章。

清单:

  1. 确定平台独立性和创建服务是否是当前的痛苦根源。
  2. 绘制出平台产品的矩阵。
  3. 定义一个工厂接口,该接口由每个产品的工厂方法组成。
  4. 为每个平台定义一个工厂派生的类,该类封装对新运算符的所有引用。
  5. 客户端应该退休的所有引用新的,并使用工厂方法来创建产品对象。

2

抽象工厂非常适合支持多种平台,同时保持您的代码库统一。假设您要在Windows,Linux和OSX上运行大型Qt或GTK +或.NET / Mono程序。但是您具有在每个平台上以不同方式实现的功能(可能通过kernel32 API或POSIX功能)。

public abstract class Feature
{
    public abstract int PlatformSpecificValue { get; }

    public static Feature PlatformFeature
    {
        get
        {
            string platform;
            // do platform detection here
            if (platform == "Win32")
                return new Win32Feature();
            if (platform == "POSIX")
                return new POSIXFeature();
        }
    }

    // platform overrides omitted
}

使用此抽象工厂,您的UI无需了解有关当前平台的任何信息。

Feature feature = Feature.PlatformFeature;
Console.WriteLine(feature.PlatformSpecificValue);

1
我不明白,这与仅使用工厂方法返回抽象类型有什么不同,以便客户端不知道实现细节?
kiwicomb123

1

如果看一下设计模式,几乎所有的设计模式都会变得多余。但是哪种模式意味着解决类似类型问题的常用方法。设计模式为您提供了一组类似类型的设计问题的设计级别方法或解决方案。使用设计模式可以帮助您解决问题,从而更快地交付。



1

我发现抽象工厂模式被高估了。

首先通常不会有要实例化的一组相互关联的类型。

其次,在进行依赖注入时,接口提供的间接(抽象)级别通常就足够了。

WindowsGui vs MacGui vs ...的典型示例通常具有WindowsButton,MacButton,WindowsScrollBar,MacScrollbar等,通过使用来宾和/或解释器模式来定义具体的 Button,Scrollbars等,通常更易于实现。实际行为。


有一个特定的目的。使用依赖项注入,您不希望服务定位器位于复合根的下方。相反,您使用注入的抽象工厂。
Adam Tuliper-MSFT 2011年

2
很好...将服务定位器与DI配合使用是一种反模式。当我们需要根据运行时值创建DEPENDENCIES时,Abstract Factory是通用解决方案。
TheMentor

1

我认为在实例化非常复杂,单个工厂过于复杂和丑陋且UI难以理解的地方,有一个抽象工厂模式而不是简单工厂模式的地方。

假设这是一个TYPE_A品牌,而不是一个单一的类。.假设有一个100种相似的Type-A类,您需要从它们中实例化一个对象。想象一下,需要一个详细的复杂信息,才能从许多类似类型的对象的品牌中制造出正确的对象,并且在该对象实体中,您需要确切地知道要调整的参数以及如何调整它们。

在该品牌的特殊工厂,我们将让他们与众不同,并获取要实例化的确切对象以及如何实例化它。我们将根据来自网络的输入(例如,在线商店中可用的颜色)以及来自在后台运行的其他应用程序和服务的输入(用户界面不知道它们的参数)知道这一点。

也许明天我们将有另一类实例化的type_B和type_C。因此,UI将具有“ if else”(如果有的话)来知道用户是否需要“ type_A”,“ type_B”或“ type_C” –但是工厂类将根据类型(来自家庭)确切地确定要构建哪个类,以及如何调整–为其参数设置什么值,或发送给承包商。所有这些-根据UI无法识别的许多参数。对于单个工厂类而言,所有这些都将太多。


0

为了直接回答您的问题,您可能无需使用这种设计模式就可以逃脱。

但是请记住,现实世界中的大多数项目都在发展,您想要提供某种可扩展性以使您的项目适应未来的发展。

根据我自己的经验,大多数时候都会实施Factory,随着项目的发展,它会变成更复杂的设计模式,例如Abstract Factory。


0

这都是关于依赖关系的。如果您不关心紧密的耦合和依赖性,那么就不需要抽象工厂。但是,一旦编写需要维护的应用程序,这将很重要。


0

假设您创建了一个.jar,而其他人使用了您的jar,并想在代码中使用一个新的具体对象。如果您不使用抽象工厂,那么她必须修改您的代码或覆盖您的代码。但是,如果您使用的是抽象工厂,那么她可以提供一个工厂并传递给您的代码,一切都很好。

改进的版本:考虑以下情况:别人写了一个框架。该框架使用一个抽象工厂和一些具体工厂在运行时创建许多对象。因此,您可以轻松地将自己的工厂注册到现有框架并创建自己的对象。该框架禁止修改,并且由于抽象工厂模式而仍然易于扩展。


0

当客户端完全不知道要创建哪种类型时,此模式特别有用。例如,假设一个专门销售手机的陈列室可以查询三星生产的智能手机。在这里,我们不知道要创建的对象的确切类型(假定电话的所有信息都以具体对象的形式包装)。但是我们知道我们正在寻找三星制造的智能手机。如果我们的设计具有Abstract工厂实施,则实际上可以利用此信息。

在C#中理解和实现抽象工厂模式


0

在System.Data.Common命名空间中提供了真实示例,该命名空间具有抽象基类,其中包括DbConnection,DbCommand和DbDataAdapter,并由.NET Framework数据提供程序(例如System.Data.SqlClient和System.Data.OracleClient)共享。使开发人员能够编写不依赖于特定数据提供者的通用数据访问代码。

DbProviderFactories类提供用于创建DbProviderFactory实例的静态方法。然后,实例根据提供程序信息和运行时提供的连接字符串返回正确的强类型对象。

例:

 DataTable allProvidersTable = DbProviderFactories.GetFactoryClasses();

在此处输入图片说明

        /* Getting SqlClient family members */
        DbProviderFactory dbProviderFactory = DbProviderFactories.GetFactory("System.Data.SqlClient");
        DbCommand dbCommand = dbProviderFactory.CreateCommand();
        DbConnection dbConnection = dbProviderFactory.CreateConnection();
        DbDataAdapter dbDataAdapter = dbProviderFactory.CreateDataAdapter();

        SqlClientFactory sqlClientFactory = (SqlClientFactory)dbProviderFactory;
        SqlConnection sqlConnection = (SqlConnection)dbConnection;
        SqlCommand sqlCommand = (SqlCommand) dbCommand;
        SqlDataAdapter sqlDataAdapter = (SqlDataAdapter) dbDataAdapter;

        /* Getting OracleClient family members*/
        dbProviderFactory = DbProviderFactories.GetFactory("System.Data.OracleClient");
        dbCommand = dbProviderFactory.CreateCommand();
        dbConnection = dbProviderFactory.CreateConnection();
        dbDataAdapter = dbProviderFactory.CreateDataAdapter();

        OracleClientFactory oracleClientFactory = (OracleClientFactory)dbProviderFactory;
        OracleConnection oracleConnection = (OracleConnection)dbConnection;
        OracleCommand oracleCommand = (OracleCommand)dbCommand;
        OracleDataAdapter oracleDataAdapter = (OracleDataAdapter)dbDataAdapter;

示例2 在此处输入图片说明

代码解决方案架构 在此处输入图片说明

具体的工厂实例使用静态工厂方法提供,如下所示

public  class FurnitureProviderFactory
{
    public static IFurnitureFactory GetFactory(string furnitureType)
    {
        if (furnitureType == "Wood")
        {
            return new WoodenFurnitureFactory();
        }
        if (furnitureType == "Plastic")
        {
            return new PlasticFurnitureFactory();
        }
        throw new Exception("Undefined Furniture");
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.