开发人员的工厂方法模式

转到Janessa Tran的个人主页 Janessa Tran BlockedUnblockFollow关注3月24日

在上一篇文章中,我们概述了设计模式的定义,它们的类别以及它们在软件开发中的重要性。在本文中,让我们深入探讨第一个被广泛使用的名为" 工厂方法模式"的 设计模式。

什么是工厂方法模式?

工厂方法模式 (或简称为工厂模式)是一种创建模式,它使用工厂方法来处理创建对象的问题,而无需指定将要创建的对象的确切类。这是通过调用工厂方法创建对象来完成的,该方法在接口中指定并由子类实现,或者在基类中实现并可选地由派生类覆盖 - 而不是通过调用构造函数。

问题

想象一下,您是一个名为LogisticsApp的物流管理应用程序的创建者。您的应用程序的第一个版本只能通过卡车处理运输,因此您当前的大部分代码都在Truck类中。

过了一段时间,您的应用程序在物流社区变得非常受欢迎。每天您都会收到海运公司提出的数十项要求,将海运物流纳入您的应用程序。

作为LogisticsApp的创建者,您对目前的成功感到非常满意。但是,在考虑将海运物流代码纳入您的应用程序的方法时,您突然意识到有许多问题等待解决。

目前,最大的问题是您当前的大部分代码已经与 Truck 类相关联。将 Ship 类添加到应用程序中需要对整个代码库进行更改。此外,如果您以后决定向应用添加其他类型的传输,则可能需要再次进行所有这些更改。 目前,为程序添加一个新类是一个很大的问题,因为其余代码已经耦合到Truck类

幸运的是,有一种设计模式可以帮助您解决这个问题,即工厂方法模式。根据其定义,工厂方法模式建议您使用特殊工厂方法调用替换直接对象构造调用(使用new运算符)。实际上,仍然通过new运算符创建对象,但是它是从工厂方法中调用的。工厂方法返回的对象通常称为 "产品"。

乍一看,这种变化可能看起来毫无意义:我们只是将构造函数调用从程序的一部分移动到另一部分。但是,请考虑这一点:现在您可以覆盖子类中的工厂方法并更改方法创建的产品类。

结构体

工厂方法模式的结构由4个重要组成部分组成:产品,混凝土产品,创造者和混凝土创造者。 工厂方法模式的结构

  • Product 声明了接口,该接口对于创建者及其子类可以生成的所有对象是通用的。
  • Concrete Products是产品界面的不同实现。
  • Creator类声明返回新产品对象的工厂方法。重要的是此方法的返回类型与产品界面匹配。
  • Concrete Creators覆盖基础工厂方法,因此它返回不同类型的产品。

请注意,工厂方法不必 始终 创建 新实例。它还可以从缓存,对象池或其他源返回现有对象。

何时使用工厂方法模式?

工厂方法模式在以下情况下真正发挥作用:

  • 不要提前知道你需要什么类对象。
  • 您希望将产品创建代码集中到程序中的一个位置,使代码更易于支持和交互。
  • 想要封装对象创建,并且不希望用户必须知道每个子类。

物流应用问题的解决方案

让我们使用工厂方法模式来解决将海运物流纳入您的LogisticsApp的问题。

我们可以看到卡车和船舶有以下相似之处:

  • 它们是不同类型的运输工具
  • 他们都提供了一些东西

考虑到这一点,我们将创建一个名为 Transport 的接口,它将提供 deliver() 方法。

 `<?php interface Transport { public function deliver(): string; } ?>` 

接下来,让我们创建两个名为Truck and Ship的具体产品类。这些类都实现了 Transport 接口,因此它们都必须覆盖 deliver 方法。

 `<?php class Truck implements Transport { public function deliver(): string { return "Truck: Deliver by land in a box."; } }` 
 `class Ship implements Transport { public function deliver(): string { return "Ship: Deliver by sea in a container."; } } ?>` 

之后,让我们创建一个名为 Logistics 的创建者类,这个类的主要职责是提供 createTransport() ---一个返回与 Transport 接口匹配的对象的抽象方法。它的定义将由具体的创建者类提供。

 `<?php abstract class Logistics { public function planDelivery(string $transportType): string { $transport = $this->createTransport($transportType); echo $transport->deliver();` 
 ` } abstract public function createTransport(string $transporType): Transport; } ?>` 

最后,让我们创建两个名为 RoadLogisticsSeaLogistics的 具体创建者类:

 `<?php class RoadLogistics extends Logistics { public function createTransport(string $transportType) { if(strtolower($transportType) === strtolower("Truck")) { return new Truck; } } }` 
 `class SeaLogistics extends Logistics { public function createTransport(string $transportType) { if(strtolower($transportType) === strtolower("Ship")) { return new Ship; } } } ?>` 

而已!我们已完成为LogisticsApp设置工厂方法模式。让我们执行下面的代码来查看最终结果:

 `$roadLogistics = new RoadLogistics; $seaLogistics = new SeaLogistics;` 
 `$transport = $roadLogistics->planDelivery("Truck"); $transport->delivery();` 
 `echo "\n";` 
 `$transport = $seaLogistics->planDelivery("Ship"); $transport->delivery();` 

结果:

 `Truck: Deliver by land in a box. Ship: Deliver by sea in a container.` 

现在,使用工厂方法的代码(通常称为客户端代码)看不到各个子类返回的实际产品之间的差异。客户端将所有产品视为抽象 传输 。物流运输的类型(RoadLogistics,SeaLogistics)将决定将使用的确切运输类型。因此,现在您可以根据需要添加任意数量的新传输类型,而无需无意中影响现有代码库。真棒! = d

工厂方法模式的利弊

优点

  • 工厂方法模式有助于防止创建者与具体产品之间的紧密耦合。
  • 使用工厂方法模式,您可以将产品创建代码集中到程序中的一个位置,使代码更易于支持和交互。
  • 通过使用工厂方法模式,您可以在不破坏现有客户端代码的情况下将新类型的产品引入程序。

缺点

  • 代码可能会变得更加复杂,因为您需要引入许多新的子类来实现该模式。

结论

在本文中,我们学到了很多关于工厂方法模式的知识。我希望这种设计模式将成为未来某个软件开发问题的完美解决方案。

在本系列的下一篇文章中,我们将了解一个名为Abstract Factory Pattern的Factory Method Pattern的兄弟。敬请关注! = d

查看英文原文

查看更多文章

公众号:银河系1号

联系邮箱:public@space-explore.com

(未经同意,请勿转载)