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

141

大多数定义都说:

抽象工厂提供了一个接口,用于创建相关对象家族,而不指定它们的具体类

抽象工厂模式的作用是什么,既然我们可以通过创建具体类的对象来实现任务?为什么要有一个创建具体类对象的工厂方法?

请给我任何一个必须要实现抽象工厂模式的现实生活例子。

15个回答

229

抽象工厂是一种非常核心的设计模式,用于依赖注入(DI)。下面是一些在Stack Overflow中应用抽象工厂被接受作为解决方案的问题列表。

据我所知,这些问题代表了人们真正关心或遇到的问题,因此这应该可以帮助你了解一些实际的例子:

  • 这是使用和测试使用工厂模式的类的正确方法吗?
  • DDD 书籍,Eric Evans:请解释“工厂应该抽象成所需的类型而不是创建具体类”是什么意思。
  • DI 容器、工厂还是 new 用于临时对象?
  • 如何对实例创建进行单元测试?
  • 用户输入的依赖注入最佳策略是什么?

  • 7
    所有这些示例都描述了工厂方法模式,因为它们都返回单个产品接口。这些示例中没有一个是抽象工厂模式,因为它们都不生成一组相关的产品接口。 - jaco0646
    46
    出于充分披露的考虑,本回答的作者应该明确指出,他也是所有链接回答的作者;因此,这个列表不是来自SO社区的代表性样本。 - jaco0646
    1
    @jaco0646 如果我没记错的话,工厂方法模式模板方法模式的一种特殊形式,它依赖于继承。不过我可能记错了,因为我现在正在旅行,没有带我的GoF书。你所说的“它们都没有产生一系列相关产品接口”是什么意思? - Mark Seemann
    1
    指示工厂不符合抽象工厂模式的最简单线索是计算工厂生产的抽象产品数量(我使用“产品接口”代替“抽象产品”,以避免过度使用“抽象”一词)。生产单个抽象产品的工厂不能是抽象工厂,因为按定义,抽象工厂生产_相关产品系列_。关键是要注意这个_系列_并不是指一个接口的不同实现,而是指具有不同、相关接口的产品。 - jaco0646
    1
    这是一个例子:http://www.oodesign.com/abstract-factory-pattern.html。这就是抽象工厂模式最初被创建的原因。 - user2802557
    显示剩余7条评论

    28
    使用抽象工厂模式的一个真实例子是提供对两个不同数据源的数据访问。假设您的应用程序支持不同的数据存储(例如 SQL 数据库和 XML 文件)。您有两个不同的数据访问接口,例如 IReadableStoreIWritableStore,定义了应用程序期望的通用方法,无论使用何种类型的数据源。
    使用哪种类型的数据源不应更改客户端代码检索其数据访问类的方式。您的 AbstractDataAccessFactory 知道配置了哪种类型的数据源,并为客户端代码提供一个具体工厂,即 SqlDataAccessFactoryXmlDataAccessFactory。这些具体工厂可以创建具体实现,例如 SqlReadableStoreSqlWriteableStore
    在 .NET Framework 中,DbProviderFactory 是此模式的一个示例。

    20
    这个回答可以准确描述工厂方法模式或静态工厂模式,但不能描述抽象工厂模式。 - jaco0646

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

    4
    作为我们可以通过创建具体类的对象来完成任务,那么抽象工厂模式有什么用呢?为什么我们需要一个创建具体类对象的工厂方法?
    在没有抽象工厂的情况下,客户端需要知道具体类的细节。这种紧耦合已经被抽象工厂所消除。
    现在,工厂方法公开了一个合同,客户必须使用它。您可以通过添加实现工厂方法公开接口的新产品来向工厂添加更多产品。
    请参考以下相关问题以获得更好的理解: 工厂模式和抽象工厂模式之间的基本区别是什么? 意图:
    提供一个接口,用于创建相关或依赖对象系列,而无需指定其具体类。

    您可以从这篇 sourcemaking 文章中了解到 抽象工厂 模式的意图、结构、检查清单和经验法则。

    检查清单:

    1. 确定当前的痛点是 平台无关性 和创建服务。
    2. 绘制一个 平台产品 的矩阵。
    3. 定义一个包含每个产品的工厂方法的 工厂接口
    4. 为每个平台定义一个派生自 工厂 的类,该类封装了所有对 new 运算符的引用。
    5. 客户端 应该放弃所有对 new 的引用,并使用 工厂方法 来创建 产品 对象。

    3

    抽象工厂对于支持多个平台且统一代码库非常有用。假设您有一个大的Qt或GTK+或.NET/Mono程序,想在Windows、Linux和OSX上运行。但是您有一个功能在每个平台上都以不同的方式实现(可能通过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
    }
    

    使用这个抽象工厂,您的用户界面不需要了解当前平台的任何信息。
    Feature feature = Feature.PlatformFeature;
    Console.WriteLine(feature.PlatformSpecificValue);
    

    3
    我不明白,这跟只使用工厂方法返回抽象类型以使客户端不了解实现细节有什么不同? - kiwicomb123

    2

    IT技术相关内容的翻译如下:

    很容易理解,如果你有一个使用抽象方法的代码,就应该创建抽象类而不是具体类。

    因为你可以更好地修改代码,所以总是要使用抽象方法进行工作。

    以下是一个很好的例子: http://en.wikipedia.org/wiki/Abstract_factory_pattern#C.23


    2

    抽象工厂或任何其他工厂,它们的存在都是为了解决同一个问题,即“对象创建的抽象化”。

    通常它会抽象出以下内容:

    1. if条件来决定要实例化哪个对象。
    2. new运算符,实例化一个对象。

    工厂的责任就是这么简单。

    您可以查看此处以获取详细说明。


    1

    如果你看一下设计模式,几乎所有的模式都可以被替代。但是,模式意味着一种常用的解决类似问题的方法。设计模式为一组相似的设计问题提供了一种设计层面的方法或解决方案。使用设计模式可以帮助你解决问题,从而更快地交付。


    1

    我认为抽象工厂模式被高估了。

    首先,你很少有一组需要实例化的相互关联的类型。

    其次,在使用依赖注入时,接口提供的间接性(抽象)通常已经足够。

    WindowsGui vs MacGui等典型示例,你可以通过定义具体的按钮、滚动条等,使用访问者和/或解释器模式来提供实际行为,这通常更容易实现。


    有一个特定的目的。使用依赖注入时,您不希望服务定位器从组合根更往下。相反,您应该使用已注入的抽象工厂。 - Adam Tuliper
    2
    嗯...在依赖注入中使用服务定位器是一种反模式。当我们需要从运行时值创建依赖项时,抽象工厂是通用解决方案。 - TheMentor

    1
    直接回答你的问题,你可能可以不使用这种设计模式。
    但是请记住,现实世界中的大多数项目都在不断发展,你希望提供某种可扩展性,以使你的项目具有未来的保障。
    根据我的经验,大多数情况下,会实现一个工厂模式,并随着项目的发展而变成更复杂的设计模式,例如抽象工厂。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接