工厂模式 - 工厂方法模式






4.88/5 (30投票s)
在本文中,我们将理解工厂方法模式。本文是工厂模式文章系列的第二部分,是第一部分:简单工厂模式的延续。
引言
在本文中,我们将理解工厂方法模式。本文是工厂模式文章系列的第二部分,是第一部分:简单工厂模式的延续。在第一部分中,我们学习了简单工厂模式。如果您还没有阅读第一部分,我建议您先阅读,然后再继续阅读本文。
在第一部分中,我们看到了简单工厂模式如何帮助我们创建对象,并发现了一些简单工厂模式存在的问题。在这一部分中,我们将学习工厂方法模式如何解决这些问题,以及何时应该使用工厂方法模式。
目录
第一部分 - 简单工厂模式
第二部分 - 工厂方法模式
- 什么是工厂方法模式
- 通过示例理解定义
- 何时使用工厂方法模式
- 工厂方法模式的优点
第三部分 - 抽象工厂模式
什么是工厂方法模式
工厂方法模式在四人帮(GoF)书中被提出,属于创建型模式。以下是从同一本书中摘录的工厂方法模式的定义:
“定义一个创建对象的接口,但让子类决定将实例化哪一个类。工厂方法让类把实例化延迟到子类。”
为了清楚地理解这个定义,我们将在本文的各个部分中分解它。
通过示例理解定义
我们将继续使用第一部分中的同一个示例,以便您了解当同一个应用程序不断增长时会出现哪种类型的问题,以及何时应该使用工厂方法模式(因为每种模式都有其自己的位置、优点和限制)。
让我们从相同的代码开始。我们将使用一个名为 IFan 的接口,它包含两个方法,如下所示。
interface IFan { void SwitchOn(); void SwitchOff(); }
最初,我们的公司打算只生产三种风扇:台扇、吊扇和排气扇。所有风扇都将具有 SwitchOn() 和 SwitchOff() 方法,并且它们的实现可能有所不同。让我们使用相同的类并实现 IFan 接口。
class TableFan : IFan {.... } class CeilingFan : IFan {.... } class ExhaustFan : IFan {..... }
到目前为止,我们编写的代码与“简单工厂模式”示例中的代码相同。
当实现工厂方法模式时,我们必须遵循下图所示的设计。
因此,我们将遵循其设计并解释工厂方法模式的标准定义。
根据定义 “定义一个创建对象的接口…”,因此让我们定义一个将包含创建风扇方法的接口。此方法在子类中的实现将包含创建具体对象的逻辑。
注意:我们正在使用 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),因此在下一篇文章中,我们将看到如何使用抽象工厂模式创建一组相关产品。感谢阅读。欢迎您的评论和改进建议。