这两种模式在意图和应用方面有什么区别?

526

工厂和抽象工厂都是创建型模式。

在意图和应用方面,这两种模式有什么区别?


14
我认为在Differences between Abstract Factory Pattern and Factory Method中,答案的质量要比这里好得多。 - KurzedMetal
1
关键区别在于工厂方法使用继承(间接性是垂直的,例如createThing()),而抽象工厂使用组合(间接性是水平的,例如getFactory().createThing())。 - David James
1
这个问题并不是一些回答者所认为的那样。不要错过Tengiz的回答,其中定义了三个不同的术语:工厂、抽象工厂和工厂方法。 - Dave Schweisguth
19个回答

448

使用工厂模式,您可以生产特定接口(例如IFruit)的实现(例如AppleBananaCherry等)的实例。

而使用抽象工厂模式,则为任何人提供了一种提供自己工厂的方法。这允许您的仓库成为IFruitFactoryIJuiceFactory,而无需仓库了解任何关于水果或果汁的信息。


5
@SPI,我觉得你误解了我的意思;工厂本身不需要实现IFruit接口——它只是实例化实现了IFruit接口的东西。当然,工厂不一定要生产符合特定接口的实例,但如果你的工厂生产的东西彼此之间完全没有关联,那可能就存在代码问题。 - John Feminella
84
生产工厂的工厂。我们需要更深入... - Paul Annekov
12
听说过比这更不正确的事情。你会如何称呼一个生产抽象工厂接口(IAbstractFactory)的工厂? - 哦,我明白了,那应该是抽象抽象工厂…… - Tengiz
3
例如,当你需要一个IFruitFactory的工厂时。正如我之前提到的,这是完全不正确的,只是对设计模式混淆的结果。我的下面的回答会澄清这一点-有抽象工厂模式,然后有工厂方法模式,然后有一些被混淆的人认为抽象工厂意味着制造其他工厂的工厂。"Factory"仅是用于表示任何现有模式的通用术语。如果需要更多详细信息,请参见下面的回答。 - Tengiz
11
这个答案完全错误!根据GoF书籍(《设计模式》),抽象工厂是一个实现了工厂接口的工厂对象,以便可以将具体工厂替换为另一个子类。它与创建工厂没有任何关系。请删除此答案,它会误导和困惑人们! - Lii
显示剩余5条评论

153

抽象工厂模式与工厂方法模式

抽象工厂模式中的方法是通过工厂方法实现的。抽象工厂模式和工厂方法模式通过抽象类型和工厂将客户端系统与实际实现类解耦。

工厂方法通过继承创建对象,而抽象工厂则通过组合创建对象。

抽象工厂模式包括AbstractFactory、ConcreteFactory、AbstractProduct、ConcreteProduct和Client。

如何实现

抽象工厂模式可以使用工厂方法模式、原型模式或单例模式进行实现。ConcreteFactory对象可以作为单例实现,因为只需要一个ConcreteFactory对象实例。

工厂方法模式是抽象工厂模式的简化版本。工厂方法模式负责创建属于一组产品的对象,而抽象工厂模式则处理多个产品族。

工厂方法使用接口和抽象类来解耦客户端和生成器类以及产生的产品。抽象工厂有一个生成器作为几个工厂方法的容器,同时还有解耦客户端和生成器以及产品的接口。

何时使用工厂方法模式

当需要将客户端与所使用的特定产品解耦时,使用工厂方法模式。使用工厂方法模式可以使客户端无需创建和配置产品实例。

何时使用抽象工厂模式

当需要将客户端与产品类解耦时,使用抽象工厂模式。特别适用于程序配置和修改。抽象工厂模式还可以强制实施有关哪些类必须与其他类一起使用的约束。创建新的ConcreteFactory可能是很费力的。

例子:

抽象工厂示例1

这个规格用于在意大利面制造机中准备不同类型的意大利面盘,是抽象工厂,而每个具体的盘子是一个工厂。

信息来源:http://java.dzone.com/news/intro-design-patterns-abstract

所有工厂(意大利面制造器的磁盘)都继承了抽象工厂的属性。每个单独的磁盘包含创建意大利面的信息,而意大利面制造器则没有。

抽象工厂示例2:

冲压设备对应抽象工厂,因为它是用于创建抽象产品对象操作的接口。冲压模具对应具体工厂,因为它们创建具体产品。每个零部件类别(引擎罩,车门等)对应抽象产品。具体元件(例如99卡美瑞的驾驶员侧门)对应具体产品。

工厂方法示例:

玩具公司对应创建者,因为它可以使用工厂来创建产品对象。制造特定类型玩具(马或汽车)的玩具公司部门对应具体创建者。


6
谢谢您讲解抽象工厂和工厂方法。我不明白在抽象工厂中我们何时使用组合来创建对象,在工厂方法中何时使用继承。如果您能发布一些代码来解释这些,那将非常有用。非常感谢。期待您的代码。再次感谢。 - Harsha
同意,如果能用简短的示例(源代码)展示组合和继承方法,会更加清晰明了。 - Aakash
示例代码@http://sourcemaking.com/design_patterns/abstract_factory - pramodc84
组合示例:public class Client { AbstractProduct product; AbstractProductAccessories accessories; public Client(AbstractFactory factory) { AbstractProduct product = factory.createProduct(); } public void run() { product.print(); accessories = product.getAccessories(); }} - Asim Ghaffar
在代码中是否有可能检测出使用了这两种模式中的哪一种? - Warlock

105

工厂模式: 工厂生产 IProduct 实现

抽象工厂模式: 一个工厂-工厂生产 IFactories, 这些工厂再生产 IProducts :)

[根据评论更新]
我之前写的不正确,至少按照Wikipedia规定是这样。抽象工厂只是一个工厂接口。通过它,你可以在运行时切换工厂,以便在不同的上下文中使用不同的工厂。例如,不同操作系统、SQL提供商、中间件驱动程序等可以使用不同的工厂。


6
好的!抽象工厂是一组工厂方法的集合,这种说法正确吗? - Warlock
2
我猜这样做是正确的,但也会失去重点 :) 一个不类比的例子可能是一个FileFactory,它有像CreateBitmapFile()或CreateTextFile()这样的方法。现在,您将向某种服务传递对该工厂的引用。但是,当您想要测试您的服务时会发生什么?您必须创建一个IFileFactory接口,以模拟对文件系统的访问。现在,在现实世界中,您可能会有一个DI/IoC框架,根据您的需求实例化IFileFactories。在这种情况下,IoC框架将充当抽象工厂。 - cwap
6
如果我理解正确,这个答案似乎暗示了抽象工厂总是会生产更多的IFactory,而这些IFactory又可以用来创建IProduct。然而,在GoF中的表述并不支持这一点,事实上与之相矛盾:抽象工厂的一个实例直接生产IProduct。换句话说,GoF中的抽象工厂不是(或者更确切地说,不需要是)“工厂-工厂”。 - SSJ_GZ
1
抽象工厂模式的定义是不正确的。一个抽象工厂包含一个或多个工厂方法,每个方法都可以从同一对象族中产生一个实例(不要与对象层次结构混淆)。虽然抽象工厂可以是工厂的工厂,但它并不一定是这样。它是相关产品的生产者。 - John Mills
2
这个答案完全是错误的!根据《设计模式》(GoF)一书,抽象工厂是一个实现工厂接口的工厂对象,以便将具体工厂替换为另一个子类。它与创建工厂没有任何关系。请删除这个答案,它会误导和混淆人们! - Lii

44

抽象工厂模式

  • 提供一个接口用于创建相关或依赖对象的家族,而不需要指定它们的具体类。

  • 抽象工厂模式与工厂方法模式非常相似。两者之间的一个区别是,在抽象工厂模式中,一个类通过组合将对象实例化的责任委托给另一个对象,而工厂方法模式使用继承,并依赖于子类处理所需的对象实例化。

  • 实际上,被委托的对象经常使用工厂方法来执行实例化!

工厂模式

  • 工厂模式是创建型模式的示例

  • 创建型模式抽象出对象实例化过程。它们隐藏了对象的创建方式,有助于使整个系统独立于其对象的创建和组成方式。

  • 类创建型模式重点在于使用继承来决定要实例化的对象,即工厂方法。

  • 对象创建型模式重点在于将实例化委托给另一个对象,即抽象工厂。

参考: 工厂模式 vs 抽象工厂模式


7
参考链接已失效。 - mkobit

42

基本区别:

工厂:创建对象而不向客户端暴露实例化逻辑。

工厂方法:定义一个用于创建对象的接口,但让子类决定实例化哪个类。

抽象工厂:为创建相关对象族提供接口。

抽象工厂模式使用组合来委托对象创建的责任。

工厂方法模式使用继承和派生类来创建对象。

示例:StaticFactory

 public class VehicleFactory {

   //Vehicle factory
   public static Vehicle getVehicle(String type){
      if(type == null){
         return null;
      }     
      if(type.equalsIgnoreCase("Car")){
         return new Car();
         
      } else if(type.equalsIgnoreCase("Truck")){
         return new Truck();
         
      } else if(type.equalsIgnoreCase("Bicycle")){
         return new Bicycle();
      }
      
      return null;
   }
}

何时使用:客户端只需要一个类,不关心它得到的是哪个具体实现。

工厂方法

何时使用:客户端不知道在运行时需要创建哪些具体类,但只想获得能够完成工作的类。

抽象工厂

何时使用:当您的系统必须创建多个产品系列而不暴露实现细节时。


41

工厂方法:你有一个工厂,创建从特定基类派生的对象。

抽象工厂:你有一个工厂,创建其他工厂,而这些工厂又生成从基类派生的对象。你这样做是因为通常不仅想创建单个对象(与工厂方法相比),而是要创建一组相关的对象。


6
这是已经被接受答案的复制,并且同样是错误的。 - jaco0646

37

抽象工厂是用于创建相关对象的接口,而工厂方法则是一种方法。抽象工厂由工厂方法实现。

enter image description here


图中的第二个标题应该写成“这两个可能是工厂方法”。如果没有看到实现,我们就不知道它们是否符合工厂方法模式。一个常见的误解是抽象工厂的方法自动成为工厂方法。这是不正确的。有两种不同的GoF模式是有原因的:它们的实现方式不同。 - jaco0646

24
很多人可能会感到惊讶,但这个问题是不正确的。如果你在面试中遇到这个问题,你需要帮助面试官理解混淆的原因。
首先,没有一个具体的模式被称为“Factory”。有一个被称为“Abstract Factory”的模式,还有一个被称为“Factory Method”的模式。
那么,“Factory”是什么意思呢?以下是其中一些(根据参考范围,所有都可以被认为是正确的):
- 一些人将其用作“Abstract Factory”的别名(快捷方式)。 - 一些人将其用作“Factory Method”的别名(快捷方式)。 - 一些人将其用作所有工厂/创建型模式的更通用名称。例如,“Abstract Factory”和“Factory Method”都是工厂。
而且,不幸的是,很多人使用“Factory”来表示另一种类型的工厂,即创建工厂或工厂(或它们的接口)。基于他们的理论:

Product 实现了 IProduct 接口,由 Factory 创建,Factory 实现了 IFactory 接口,由 AbstractFactory 创建。

要理解这是多么荒谬,请继续我们的方程:

AbstractFactory 实现了 IAbstractFactory 接口,由……AbstractAbstractFactory???

希望你能明白这个观点。不要感到困惑,也请不要为没有原因的东西发明名称。

-

P.S.: 产品的工厂是抽象工厂,而抽象工厂的工厂也将成为抽象工厂的另一个例子。


我该如何区分创建其他抽象工厂的抽象工厂和创建特定对象的抽象工厂?是使用GenericAbstractFactory还是AbstractFactoryFactory? - Andrew
在设计模式中并不存在这样的事情。它们都是AbstractFactory模式的实例。因此,一个AbstractFactory创建特定的对象,另一个AbstractFactory创建工厂(这些工厂再次是AbstractFactory)。 - Tengiz
当然。那么我应该如何为这些执行不同任务的类命名呢?因为创建其他工厂和创建其他(简单)对象是两回事。我不关心模式,我需要可读性强的代码。 - Andrew
3
易读的代码是意图清晰的代码。在命名类时,除非非常必要,否则不应该提及太多模式。例如,如果你有一个创建不同运输方式的抽象工厂,可以称之为TransportCreator或TransportFactory,甚至可以称之为TransportManufacturer。然后,如果你有这些工厂的工厂,可以根据业务需要进行命名,而不是基于它们实现的模式。如果想要开启新的制造商,可能可以称之为ManufacturerManagement。基本上,按照业务名称来命名事物,而不是基于它们实现的模式。 - Tengiz

23

抽象工厂示例/场景

我住在一个有雨季、冬天下雪和夏天炎热晴朗的地方。为了保护自己免受自然元素的侵害,我需要不同种类的衣物。为此,我去离家近的商店,并要求购买适合各种环境和我的经济能力的防护用品。店主根据我的标准提供我相应的物品,这些物品具有相同的质量和价位范围。由于他了解我的标准,所以很容易做到这一点。但是当来自街对面的富人有同样的需求时,他会得到昂贵的名牌商品。值得注意的是,他给我所有的物品在质量、标准和成本方面都相互补充。可以说它们彼此搭配。富人得到的物品也是如此。

通过上述情况的观察,我现在欣赏店主的效率。我可以将这个店主替换为抽象店铺。我们使用抽象产品与我和富人作为潜在客户获取物品。我们只需要适合我们需求的产品/物品。

现在,我可以很容易地想象自己考虑一个为其大量客户提供一套服务的在线商店。每个客户都属于三个群体中的一个。当一个高级用户打开该网站时,他会得到出色的用户界面、高度定制的广告窗格、菜单中更多的选项等等。这些同样的功能也被呈现给金牌用户,但是菜单中的功能较少,广告主要与相关内容相关,用户界面略显笨拙。最后是我这类‘免费组’用户, 我只需获得足够的服务以避免冒犯。用户界面是最基本的, 广告不知所云, 菜单只有登出。

如果我有机会构建像这样的网站,我肯定会考虑抽象工厂模式。

抽象产品: 广告窗格,菜单,用户界面。
抽象工厂: 网上商店用户体验
具体工厂:高级用户体验、金牌用户体验、普通用户体验。


抽象工厂的场景很好,但你并没有真正回答问题,工厂和抽象工厂之间有什么区别。 - Adelin

17
//Abstract factory - Provides interface to create factory of related products
interface PizzaIngredientsFactory{    
   public Dough createDough(); //Will return you family of Dough
   public Clam createClam();   //Will return you family of Clam
   public Sauce createSauce(); //Will return you family of Sauce
}

class NYPizzaIngredientsFactory implements PizzaIngredientsFactory{

   @Override
   public Dough createDough(){
      //create the concrete dough instance that NY uses
      return doughInstance;
   }

   //override other methods
} 

其他答案已经提供了教科书式的定义,我想我也提供一个例子。

所以在这里,PizzaIngredientsFactory 是一个抽象工厂,因为它提供了创建一组相关产品的方法。

请注意,抽象工厂中的每个方法本身都是一个工厂方法。例如,createDough() 本身就是一个工厂方法,其具体实现将由子类如 NYPizzaIngredientsFactory 提供。因此,使用这种方式,每个不同的位置都可以创建属于其位置的具体成分实例。

工厂方法

提供具体实现的实例

例如:
- createDough() - 提供了面团的具体实现。因此这是一个工厂方法。

抽象工厂

提供接口以创建相关对象的族群

例如:
- PizzaIngredientsFactory 是一个抽象工厂,因为它允许创建一组相关的对象,如 DoughClamsSauce。对于创建每个对象族群,它提供了一个工厂方法。

摘自《Head First设计模式》中的示例。


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