抽象工厂模式和工厂模式有何区别?

610

我知道有很多关于这两种模式之间区别的帖子,但是有一些东西我找不到。

从我所阅读的内容来看,工厂方法模式允许您定义如何创建单个具体产品,但将实现隐藏在客户端外,因为他们将看到通用产品。我的第一个问题是关于抽象工厂的。它的作用是允许您创建依赖于您使用的特定工厂的多组具体对象,而不仅仅是单个具体对象吗?抽象工厂只返回一个非常大的对象还是许多对象,取决于您调用的方法?

我最后的两个问题都涉及到我无法完全理解的一个引号,在许多地方看到:

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

我的理解是,工厂方法模式具有一个Creator接口,使ConcreteCreator负责知道要实例化哪个ConcreteProduct。这就是它使用继承处理对象实例化的含义吗?

现在就那个引号而言,抽象工厂模式如何通过组合将对象实例化的责任委托给另一个对象?这是什么意思?在我看来,抽象工厂模式也使用继承来执行构建过程,但我仍在学习这些模式。

特别是对于最后一个问题的任何帮助,将不胜感激。


从客户端的角度看,“实例是如何创建的”,这会帮助你理解这个引用。 - Karthik Bose
4
@nawfal,那个帖子中的答案很糟糕。 - jaco0646
21个回答

674

它们之间的区别

"工厂方法"和"抽象工厂"的主要区别在于,工厂方法是一个方法,而抽象工厂是一个对象。很多人混淆这两个术语,并开始交替使用它们。我记得当我学习它们时,我很难找到确切的区别。

因为工厂方法只是一个方法,它可以在子类中被重写,因此您引用的后半部分说:

... 工厂模式使用继承并依赖子类来处理所需的对象实例化。

引用假定一个对象在这里调用了它自己的工厂方法。因此,唯一能够改变返回值的是子类。

抽象工厂是一个具有多个工厂方法的对象。看看你引用的第一部分:

... 在抽象工厂模式中,一个类通过组合将对象实例化的责任委托给另一个对象 ...

他们的意思是有一个对象A,它想要创建一个Foo对象。它不会自己创建Foo对象(例如,使用工厂方法),而是使用一个不同的对象(抽象工厂)来创建Foo对象。

代码示例

为了向您展示区别,这里是一个使用工厂方法的示例:

class A {
    public void doSomething() {
        Foo f = makeFoo();
        f.whatever();   
    }

    protected Foo makeFoo() {
        return new RegularFoo();
    }
}

class B extends A {
    protected Foo makeFoo() {
        //subclass is overriding the factory method 
        //to return something different
        return new SpecialFoo();
    }
}

以下是一个使用抽象工厂的示例:

class A {
    private Factory factory;

    public A(Factory factory) {
        this.factory = factory;
    }

    public void doSomething() {
        //The concrete class of "f" depends on the concrete class
        //of the factory passed into the constructor. If you provide a
        //different factory, you get a different Foo object.
        Foo f = factory.makeFoo();
        f.whatever();
    }
}

interface Factory {
    Foo makeFoo();
    Bar makeBar();
    Aycufcn makeAmbiguousYetCommonlyUsedFakeClassName();
}

//need to make concrete factories that implement the "Factory" interface here

45
这是一个很好的解释。但是,最重要的部分没有回答,那就是:什么时候使用其中之一,以及什么时候使用另一种模式? - croraf
23
不确定这是否正确。相当确定工厂方法是一种以工厂方法命名的设计模式,但涉及类结构和继承。它不是单独的方法。 - Aviv Cohn
3
工厂方法可以是所有常规类中具有不同目的的方法。但是抽象工厂是由客户端使用的类/对象,仅负责在某个产品族中创建一些产品。所述说法正确吗? - Hieu Nguyen
1
回答 Aviv Cohn,这是一个单一的方法名称,在子类中有多个重载。它不是一个独立的单一方法定义。示例代码通过两个 makeFoo 实现演示了这一点。 - Tom Dalling
1
@croraf 工厂模式就像任何杂货公司的特许经营系统。你购买许可证和基础设施,他们有一定的框架可以允许你运作,然后你可以用自己的名字但同样使用他们的品牌开设自己的杂货店。工厂方法更像是德国汽车制造商的品牌。比如大众拥有SEAT,它是一个子公司,使用相同的零部件并让SEAT处理他们自己的流程。具体执行方式由公司自行决定,如何推销他们的品牌、建造哪种型号、吸引哪些客户、以及内部运作方式等等。 - Dr4gon
显示剩余3条评论

155

抽象工厂创建一个基类并定义了创建对象的抽象方法。派生自基类的每个工厂类都可以为每种对象类型创建自己的实现。

图片描述

工厂方法是一个简单的方法,用于在类中创建对象。通常将其添加到聚合根(例如Order类有一个名为CreateOrderLine的方法)中。

图片描述

抽象工厂

在下面的示例中,我们设计了一个接口,以便可以将队列创建与消息系统解耦,并因此可以创建不同队列系统的实现,而无需更改代码库。

interface IMessageQueueFactory
{
  IMessageQueue CreateOutboundQueue(string name);
  IMessageQueue CreateReplyQueue(string name);
}

public class AzureServiceBusQueueFactory : IMessageQueueFactory
{
      IMessageQueue CreateOutboundQueue(string name)
      {
           //init queue
           return new AzureMessageQueue(/*....*/);
      }

      IMessageQueue CreateReplyQueue(string name)
      {
           //init response queue
           return new AzureResponseMessageQueue(/*....*/);
      }

}

public class MsmqFactory : IMessageQueueFactory
{
      IMessageQueue CreateOutboundQueue(string name)
      {
           //init queue
           return new MsmqMessageQueue(/*....*/);
      }

      IMessageQueue CreateReplyQueue(string name)
      {
           //init response queue
           return new MsmqResponseMessageQueue(/*....*/);
      }
}

工厂方法

HTTP服务器面临的问题在于我们必须为每个请求提供响应。

public interface IHttpRequest
{
    // .. all other methods ..

    IHttpResponse CreateResponse(int httpStatusCode);
}

如果没有工厂方法,HTTP服务器的用户(即程序员)将被迫使用特定于实现的类,这违背了 IHttpRequest 接口的目的。

因此,我们引入了工厂方法,以便将响应类的创建也抽象化。

总结

区别在于包含工厂方法的类的预期目的不是创建对象,而抽象工厂应仅用于创建对象。

在使用工厂方法时应当小心,因为在创建对象时容易破坏LSP(里氏替换原则)。


4
为什么我们需要混凝土产品? - Andrew S
102
因为没有人愿意投资于想法。 - jgauffin
7
抽象工厂应该创建不止 Button() 来制作“相关产品族”。例如,经典的 GoF 示例创建了 ScrollBar()Window()。优点是抽象工厂可以在其多个产品之间执行通用主题。 - jaco0646
2
@AndrewS:回答你的问题。如果我们不需要为相同的抽象(接口)拥有不同的具体产品(类),那么我们可能需要建造者模式而不是工厂模式。(迟到总比不到好;) - jgauffin
1
使用工厂方法模式时,如何违反Liskov替换原则? - Emre Tapcı
显示剩余4条评论

143
抽象工厂工厂方法设计模式的区别如下:
  • 工厂方法用于创建一个产品,而抽象工厂则是创建相关或依赖产品族。
  • 工厂方法模式向客户端公开一个方法来创建对象,而在抽象工厂中,他们公开一组构成这些工厂方法的相关对象。
  • 工厂方法模式隐藏单个对象的构建,而抽象工厂则隐藏相关对象族的构建。通常使用(一组)工厂方法实现抽象工厂。
  • 抽象工厂模式使用组合将创建对象的责任委托给另一个类,而工厂方法设计模式使用继承并依赖于派生类或子类来创建对象。
  • 工厂方法模式背后的思想是,它允许客户端不知道在运行时需要创建哪些具体类,但只是想得到能够完成工作的类,而抽象工厂模式最好在系统必须创建多个产品系列或者你想提供一个产品库而不暴露实现细节时使用。
工厂方法模式实现:Factory Method UML 抽象工厂模式实现:Abstract Factory UML

20
抽象工厂示例我不太确定。我认为形状工厂和颜色工厂应该实现相同的方法。但是如果我是对的,那么这个示例就没有意义了。 - Joaquin Iurchuk
4
要点正确,但两张图完全错误且非常误导。请看 @Trying 提供的以下图示,其中提供了一个准确的抽象工厂模型。 - jaco0646
1
我必须承认这两个图表确实非常误导人。我在tutorialspoint网站上看到过它们,说实话我并不完全同意它们。但是说明看起来不错。 - SoftwareDeveloper
这非常具有误导性。 - diyoda_
1
50个以上的赞和图表都是错误的。这证明了你不能信任许多SO上的设计模式答案。 - Fuhrmanator
尽管看起来非常简单,但似乎违反了LSP原则。 - merinoff

88
抽象工厂和工厂方法的主要区别在于抽象工厂通过组合实现,而工厂方法通过继承实现
是的,你没看错:这两种模式之间的主要区别就是老生常谈的组合 vs 继承争论。
UML图可以在(GoF)书中找到。我想提供代码示例,因为我认为将本线程中前两个答案的示例结合起来会比任何一个单独的答案更好地演示。此外,我已经在类和方法名称中使用了书中的术语。 抽象工厂
  1. 最重要的一点是要理解抽象工厂被注入到客户端中。这就是为什么我们说抽象工厂是通过组合实现的。通常,依赖注入框架会执行此任务;但并不需要框架来进行DI。
  2. 第二个关键点是这里的具体工厂不是工厂方法的实现!工厂方法的示例代码将在下面展示。
  3. 最后,需要注意的第三点是产品之间的关系:在这种情况下是出站队列和回复队列。一个具体工厂生产Azure队列,另一个生产MSMQ。GoF将这种产品关系称为“家族”,重要的是要意识到这里的家族并不意味着类层次结构。
public class Client {
    private final AbstractFactory_MessageQueue factory;

    public Client(AbstractFactory_MessageQueue factory) {
        // The factory creates message queues either for Azure or MSMQ.
        // The client does not know which technology is used.
        this.factory = factory;
    }

    public void sendMessage() {
        //The client doesn't know whether the OutboundQueue is Azure or MSMQ.
        OutboundQueue out = factory.createProductA();
        out.sendMessage("Hello Abstract Factory!");
    }

    public String receiveMessage() {
        //The client doesn't know whether the ReplyQueue is Azure or MSMQ.
        ReplyQueue in = factory.createProductB();
        return in.receiveMessage();
    }
}

public interface AbstractFactory_MessageQueue {
    OutboundQueue createProductA();
    ReplyQueue createProductB();
}

public class ConcreteFactory_Azure implements AbstractFactory_MessageQueue {
    @Override
    public OutboundQueue createProductA() {
        return new AzureMessageQueue();
    }

    @Override
    public ReplyQueue createProductB() {
        return new AzureResponseMessageQueue();
    }
}

public class ConcreteFactory_Msmq implements AbstractFactory_MessageQueue {
    @Override
    public OutboundQueue createProductA() {
        return new MsmqMessageQueue();
    }

    @Override
    public ReplyQueue createProductB() {
        return new MsmqResponseMessageQueue();
    }
}

工厂方法

  1. 最重要的一点是要理解ConcreteCreator就是客户端。换句话说,客户端是一个子类,其父类定义了factoryMethod()。这就是为什么我们说工厂方法是通过继承来实现的。
  2. 第二个关键点是记住,工厂方法模式只是模板方法模式的一个特化。两种模式具有相同的结构,只是目的不同。工厂方法是创建性模式(它创建某些东西),而模板方法是行为性模式(它计算某些东西)。
  3. 最后需要注意的第三点是,Creator(父类)调用自己的factoryMethod()。如果我们从父类中删除anOperation(),只留下一个方法,则不再是工厂方法模式。换句话说,工厂方法模式在父类中不能少于两个方法;并且一个方法必须调用另一个方法。
public abstract class Creator {
    public void anOperation() {
        Product p = factoryMethod();
        p.whatever();
    }

    protected abstract Product factoryMethod();
}

public class ConcreteCreator extends Creator {
    @Override
    protected Product factoryMethod() {
        return new ConcreteProduct();
    }
}

其他工厂模式

请注意,虽然GoF定义了两种不同的工厂模式,但这并不是唯一存在的工厂模式。它们甚至不一定是最常用的工厂模式。著名的第三个例子是Josh Bloch在《Effective Java》中提出的静态工厂模式。《Head First 设计模式》一书包括了另一种他们称之为简单工厂的模式。

不要陷入认为每个工厂模式都必须与GoF中的一个匹配的陷阱中。


5
基于很好的例子,回答非常出色和清晰,我认为这是这个话题中最好的答案。 - Luke Duda
1
很好的解释。Factory方法必须调用其抽象工厂方法点,+1。有了这个点,就更清楚了,如果不理解这个点:如果我们的工厂方法没有被自己调用,那么它将被某个其他类使用,该类将组合它并注入其子类,它就变成了抽象工厂,如果不理解抽象工厂方法必须由工厂本身调用,就像模板方法模式一样,区别就变得不太清晰了。 - nits.kk
还有一个问题-备注。在“工厂方法”模式中,factoryMethod()方法是否应该始终是protected方法?(我认为是的) - Yaroslav Fedoruk
3
@YaroslavFedoruk,《设计模式》一书允许使用public工厂方法,而且该方法甚至不需要是abstract的;但是关键点在于该方法用于继承,因此它不能(例如)是staticfinal。我将该方法设置为protectedabstract,以突出显示(必需的)可扩展性。 - jaco0646
如果我们从父类中删除anOperation(),只留下一个方法,那么它就不再是工厂方法模式了。@jaco0646 我们可以说它会变成只有一个方法的抽象工厂吗? - vborutenko
显示剩余4条评论

46

抽象工厂是一个用于创建相关产品的接口,但工厂方法只是其中的一种方法。抽象工厂可以通过多个工厂方法来实现。

抽象工厂UML图


11
您已经在这里发布了相同的答案(https://dev59.com/x3NA5IYBdhLWcg3wVcT6#20648551)。如果您认为这个问题类似,请将其标记为重复,而不是重复回答。 - Ja͢ck
非常清晰。这应该被选为这个问题的答案。当前选择的答案有冗余的解释。 - Changdae Park
2
图中的第二个标题应该写成“这两个可能是工厂方法”。如果没有看到实现,我们就不知道它们是否符合工厂方法模式。一个常见的误解是抽象工厂的方法自动成为工厂方法。这是不正确的。有两种不同的GoF模式是有原因的:它们的实现方式不同。 - jaco0646
这确实帮助我理解它,它很像我在书中看到的例子。但我仍然不确定我为什么要这样做。但这不是问题的关键... - Chuckk Hubbard
除了工厂方法,还有哪些实现抽象工厂的方法?单例模式可能是一种选择吗?您能否再举几个例子呢?@Trying - Ali Mert Çakar

14

考虑以下示例以便更好理解。

电信公司提供什么?例如宽带、电话线和移动服务,您被要求创建一个应用程序来向他们的客户提供这些产品。

通常情况下,您需要通过工厂方法创建产品,如宽带、电话线和移动服务。你知道这些产品的属性,并且这样做非常简单明了。

现在,公司想要为客户提供一组捆绑销售的产品,例如宽带、电话线和移动服务,这时就可以使用抽象工厂了。

抽象工厂是其他工厂的组合,它们各自负责创建自己的产品。在此过程中,抽象工厂知道如何将这些产品放置在更有意义的位置上,从而履行自己的职责。

在这种情况下,BundleFactory是抽象工厂,BroadbandFactoryPhonelineFactoryMobileFactory是工厂。更简单地说,这些工厂将具有工厂方法来初始化各个产品。

请参见下面的代码示例:

public class BroadbandFactory : IFactory {
    public static Broadband CreateStandardInstance() {
        // broadband product creation logic goes here
    }
}

public class PhonelineFactory : IFactory {
    public static Phoneline CreateStandardInstance() {
        // phoneline product creation logic goes here
    }
}

public class MobileFactory : IFactory {
    public static Mobile CreateStandardInstance() {
        // mobile product creation logic goes here
    }
}

public class BundleFactory : IAbstractFactory {

    public static Bundle CreateBundle() {
        broadband = BroadbandFactory.CreateStandardInstance();
        phoneline = PhonelineFactory.CreateStandardInstance();
        mobile = MobileFactory.CreateStandardInstance();

        applySomeDiscountOrWhatever(broadband, phoneline, mobile);
    }

    private static void applySomeDiscountOrWhatever(Broadband bb, Phoneline pl, Mobile m) {
        // some logic here
        // maybe manange some variables and invoke some other methods/services/etc.
    }
}

希望这有所帮助。


1
GoF工厂模式中没有“static”方法。这是错误的。 - jaco0646

10

工厂方法模式依赖于继承:将对象的创建委托给子类,子类实现工厂方法以创建对象。

抽象工厂模式依赖于对象组合:对象的创建通过在工厂接口中公开的方法来实现。

工厂和抽象工厂模式的高级图示如下:

diagram

要了解有关工厂方法的更多信息,请参阅本文

要了解有关抽象工厂方法的更多信息,请参阅本文


8

现实生活中的例子(易于记忆)

工厂

想象一下,你正在建造一座房子,你向木匠要门。你给出门的尺寸和要求,他将为你制作一扇门。在这种情况下,木匠就是门的工厂。你的规格是工厂的输入,门是工厂的输出或产品。

抽象工厂

现在,考虑同样的门的例子。你可以去找木匠,也可以去找塑料门店或PVC门店。所有这些都是门的工厂。根据情况,你决定需要接触哪种类型的工厂。这就像一个抽象工厂。

我在这里解释了工厂方法模式和抽象工厂模式,开始时没有使用它们来解释问题,然后通过使用上述模式来解决问题。 https://github.com/vikramnagineni/Design-Patterns/tree/master


1
这是不正确的。这是一个非常普遍的误解,即抽象工厂只是一个工厂的工厂。 - jaco0646

4

1. 我的第一个问题是关于抽象工厂模式的。它的作用是允许您创建具体对象的家族(可以取决于您使用的特定工厂)而不仅仅是单个具体对象吗?

是的。抽象工厂的意图是:

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


2. 抽象工厂只返回一个非常大的对象还是根据调用的方法返回多个对象?

理想情况下,它应该根据客户端调用的方法返回一个对象。

3. 我的理解是,工厂方法模式有一个创建者接口,将具体创建者负责知道要实例化哪个具体产品。这是否意味着使用继承来处理对象实例化?

是的。工厂方法使用继承。

4. 抽象工厂模式通过组合将对象实例化的责任委托给另一个对象?这是什么意思?

AbstractFactory定义了一个FactoryMethod,ConcreteFactory负责构建ConcreteProduct。

只需按照James Sugrue在dzone文章中的代码示例即可。

您可以在相关SE帖子中找到更多详细信息:

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

设计模式:工厂 vs 工厂方法 vs 抽象工厂


4

有许多定义。通常,描述工厂模式的三种常见方式如下:

  1. 简单工厂

一种基于条件的简单对象创建方法/类。

  1. 工厂方法

使用子类提供实现的工厂方法设计模式。

  1. 抽象工厂

抽象工厂设计模式生产相关或依赖对象系列,而不指定它们的具体类。

以下链接非常有用- 工厂比较 - refactoring.guru


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