65.9K
CodeProject 正在变化。 阅读更多。
Home

工厂模式 - 工厂方法模式

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (30投票s)

2016年10月2日

CPOL

5分钟阅读

viewsIcon

61442

downloadIcon

631

在本文中,我们将理解工厂方法模式。本文是工厂模式文章系列的第二部分,是第一部分:简单工厂模式的延续。

引言

在本文中,我们将理解工厂方法模式。本文是工厂模式文章系列的第二部分,是第一部分:简单工厂模式的延续。在第一部分中,我们学习了简单工厂模式。如果您还没有阅读第一部分,我建议您先阅读,然后再继续阅读本文。

在第一部分中,我们看到了简单工厂模式如何帮助我们创建对象,并发现了一些简单工厂模式存在的问题。在这一部分中,我们将学习工厂方法模式如何解决这些问题,以及何时应该使用工厂方法模式。

目录

第一部分 - 简单工厂模式

第二部分 - 工厂方法模式

  • 什么是工厂方法模式
  • 通过示例理解定义
  • 何时使用工厂方法模式
  • 工厂方法模式的优点

第三部分 - 抽象工厂模式

什么是工厂方法模式

工厂方法模式在四人帮(GoF)书中被提出,属于创建型模式。以下是从同一本书中摘录的工厂方法模式的定义:

“定义一个创建对象的接口,但让子类决定将实例化哪一个类。工厂方法让类把实例化延迟到子类。”

为了清楚地理解这个定义,我们将在本文的各个部分中分解它。

通过示例理解定义

我们将继续使用第一部分中的同一个示例,以便您了解当同一个应用程序不断增长时会出现哪种类型的问题,以及何时应该使用工厂方法模式(因为每种模式都有其自己的位置、优点和限制)。

让我们从相同的代码开始。我们将使用一个名为 IFan 的接口,它包含两个方法,如下所示。

    interface IFan
    {
        void SwitchOn();
        void SwitchOff();
    }

最初,我们的公司打算只生产三种风扇:台扇、吊扇和排气扇。所有风扇都将具有 SwitchOn() 和 SwitchOff() 方法,并且它们的实现可能有所不同。让我们使用相同的类并实现 IFan 接口。

    class TableFan : IFan {.... }

    class CeilingFan : IFan {.... }

    class ExhaustFan : IFan {..... }

到目前为止,我们编写的代码与“简单工厂模式”示例中的代码相同。

当实现工厂方法模式时,我们必须遵循下图所示的设计。

Factory Method Classes

因此,我们将遵循其设计并解释工厂方法模式的标准定义。

根据定义 “定义一个创建对象的接口…”,因此让我们定义一个将包含创建风扇方法的接口。此方法在子类中的实现将包含创建具体对象的逻辑。

注意:我们正在使用 IFanFactory 接口,但也可以使用抽象类而不是接口。抽象类至少需要有一个未实现的抽象方法,该方法将在子类中实现以创建对象。

    interface IFanFactory
    {
        IFan CreateFan();
    }
     

由于设计模式只是一种特定的设计方式,并且与语言无关,因此我们也可以使用另一种方法来定义对象创建的基本联系,即“定义一个创建对象的接口”。例如,在JavaScript中,我们没有抽象类或接口的概念,但仍然可以使用设计模式。有关更多信息,请查看Rob Dodson 的这篇博客文章

继续定义:“......,让子类决定将实例化哪一个类……”:让我们创建子类,它们将决定使用哪个具体类来创建实例。

要创建三种类型的对象,即台扇、吊扇和排气扇,我们需要创建三种类型的工厂或 IFanFactory 的子类。这三个工厂将实现 IFanFactory 接口。

    class TableFanFactory : IFanFactory {....}

    class CeilingFanFactory : IFanFactory {....}

    class ExhaustFanFactory : IFanFactory {....}

再次继续定义:“...工厂方法让类把实例化延迟到子类。”:这里 IFan CreateFan(); 是一个工厂方法。在具体工厂(如 TableFanFactory 等)的 CreateFan 方法(即工厂方法)内部,我们指定了将实例化哪个确切的类来创建 Fan 对象。因此,IFanFactory 将具体类的决策推迟到其子类,即 TableFanFactory、CeilingFanFactory 和 ExhaustFanFactory。

现在客户端实现如下所示。

    
    //The client code is as follows:
    static void Main(string[] args)
    {
         IFanFactory fanFactory = new PropellerFanFactory();

         // Creation of a Fan using Factory Method Pattern
         IFan fan = fanFactory.CreateFan();

         // Use created object 
         fan.SwitchOn();

         Console.ReadLine();
     }    

何时使用工厂方法模式

在上面的示例中,为了创建三种类型的对象(台扇、吊扇和排气扇),我们创建了三个工厂类,但在学习“简单工厂模式”时,我们只创建了一个工厂。这是因为我们这里遵循了工厂方法模式的规则。现在的问题是,为什么我们需要使用工厂方法模式,因为我们已经可以使用“简单工厂模式”来实现这一点。假设公司将推出一种名为“螺旋桨风扇”的新风扇。这是一个新需求。使用工厂方法模式,我们不会像在“简单工厂模式”中那样在 switch case 中添加新条件。我们只需创建一个名为“PropellerFan”的新类,并像处理其他风扇一样让它继承 IFan 接口。

    class PropellerFan : IFan {..... }

然后,我们将创建一个名为 PropellerFanFactory 的新工厂,并让它继承 IFanFactory。当客户端想要创建 PropellerFan 类的实例时。只需创建 PropellerFanFactory 的实例,客户端就可以获得 PropellerFan 的实例。

工厂方法模式的优点

  • 在上一节中,我们看到工厂方法模式遵循开闭原则。当出现新需求时,我们没有更改现有代码,而是需要创建一个附加的工厂。
  • 与简单工厂模式相比,使用工厂方法模式编写单元测试用例更容易,因为不使用 switch case(或长 if else 块)。
  • 为了支持其他产品,我们不修改现有代码,只需添加一个新工厂类,因此无需重新运行现有的旧单元测试。
  • 客户端调用 CreateFan(工厂方法),而无需知道实际创建的对象是如何以及以何种类型创建的。
  • 如果我们使用抽象类(如 BaseFanFactory)(而不是 IFanFactory),我们可以在 BaseFanFactory 抽象类中提供通用方法的实现,只将 CreateFan 方法声明为抽象。根据需求,我们可以在 BaseFanFactory 中拥有更多抽象方法。

结论

在本文中,我们通过一个示例学习了工厂方法模式及其用法。我们理解了工厂方法模式的上下文以及它与简单工厂模式的区别。但是,它只能创建一种产品(在我们示例中都实现了 IFan),因此在下一篇文章中,我们将看到如何使用抽象工厂模式创建一组相关产品。感谢阅读。欢迎您的评论和改进建议。

参考文献

  1. 关于简单工厂动机的讨论
  2. Corey Broderick 的博客
  3. Coing Geek 博客
  4. 关于工厂模式的文章
© . All rights reserved.