抽象工厂模式
首先,如果你在阅读本书之前读过一些背景资料,你一定听说过 "具体类" 这个词。这是什么意思呢?简单地说,它与抽象类正好相反;它是一个你可以实例化以创建对象的类。
抽象工厂由以下类组成:抽象工厂、具体工厂、抽象产品、具体产品和我们的客户端。
在工厂模式中,我们生产特定接口的实现(例如,notifier
是我们的接口,而 e-mail
、SMS
和 post
是我们的具体实现)。使用抽象工厂模式,我们将创建一个工厂接口的实现,每个工厂都知道如何创建自己的产品。
假设我们有两个玩具工厂,一个在旧金山,一个在伦敦。它们都知道如何在两个地点创建两家公司的产品。
有鉴于此,我们的 ToyFactory
接口看起来是这样的:
Unresolved include directive in modules/ROOT/pages/ch03/ch3-04.adoc - include::example$/Chapter 3/Abstract/ToyFactory.php[]
现在已经完成了,我们可以建造我们的旧金山玩具工厂(SFToyFactory
)作为我们的具体工厂类:
Unresolved include directive in modules/ROOT/pages/ch03/ch3-04.adoc - include::example$/Chapter 3/Abstract/SFToyFactory.php[]
现在我们可以添加我们的英国玩具工厂(UKToyFactory
):
Unresolved include directive in modules/ROOT/pages/ch03/ch3-04.adoc - include::example$/Chapter 3/Abstract/UKToyFactory.php[]
正如你所注意到的,我们在 Toys
命名空间中创建了各种玩具,所以现在我们可以为我们的玩具组合抽象方法了。让我们从 Toy
类开始。每个玩具最终都会扩展这个类:
<?php
namespace Toys;
abstract class Toy
{
abstract public function getSize(): int;
abstract public function getPictureName(): string;
}
现在,对于我们在 ToyFactory
接口中声明的两种类型的玩具(迷宫,maze和拼图,puzzle),我们可以从 Maze
类开始声明它们的抽象方法:
<?php
namespace Toys;
abstract class MazeToy extends Toy
{
private $type = "Maze";
}
现在让我们开始 Puzzle 类:
<?php
namespace Toys;
abstract class PuzzleToy extends Toy
{
private $type = "Puzzle";
}
现在是我们具体类的时候了,所以让我们从旧金山的实现开始。
SFMazeToy
的代码如下:
<?php
namespace Toys;
class SFMazeToy extends MazeToy
{
private $size;
private $pictureName;
public function __construct()
{
$this->size = 9;
$this->pictureName = "San Francisco Maze";
}
public function getSize(): int
{
return $this->size;
}
public function getPictureName(): string
{
return $this->pictureName;
}
}
下面是 SFPuzzleToy
类的代码,它是迷宫(Maze
)玩具类的不同实现:
<?php
namespace Toys;
class SFPuzzleToy extends PuzzleToy
{
private $size;
private $pictureName;
public function __construct()
{
$rand = rand(1, 3);
switch ($rand) {
case 1:
$this->size = 3;
break;
case 2:
$this->size = 6;
break;
case 3:
$this->size = 9;
break;
}
$this->pictureName = "San Francisco Puzzle";
}
public function getSize(): int
{
return $this->size;
}
public function getPictureName(): string
{
return $this->pictureName;
}
}
现在我们可以通过英国工厂的实施来完成这个任务。
让我们首先为迷宫玩具实现 UKMazeToy.php
:
<?php
namespace Toys;
class UKMazeToy extends Toy
{
private $size;
private $pictureName;
public function __construct()
{
$this->size = 9;
$this->pictureName = "London Maze";
}
public function getSize(): int
{
return $this->size;
}
public function getPictureName(): string
{
return $this->pictureName;
}
}
我们也为拼图玩具创建一个类,UKPuzzleToy.php
:
<?php
namespace Toys;
class UKPuzzleToy extends PuzzleToy
{
private $size;
private $pictureName;
public function __construct()
{
$rand = rand(1, 2);
switch ($rand) {
case 1:
$this->size = 3;
break;
case 2:
$this->size = 9;
break;
}
$this->pictureName = "London Puzzle";
}
public function getSize(): int
{
return $this->size;
}
public function getPictureName(): string
{
return $this->pictureName;
}
}
现在; 让我们将所有这些放在我们的 index.php
文件中:
<?php
require_once('ToyFactory.php');
require_once('Toys/Toy.php');
require_once('Toys/MazeToy.php');
require_once('Toys/PuzzleToy.php');
require_once('SFToyFactory.php');
require_once('Toys/SFMazeToy.php');
require_once('Toys/SFPuzzleToy.php');
$sanFraciscoFactory = new SFToyFactory();
var_dump($sanFraciscoFactory->makeMaze());
echo "\n";
var_dump($sanFraciscoFactory->makePuzzle());
echo "\n";
require_once('UKToyFactory.php');
require_once('Toys/UKMazeToy.php');
require_once('Toys/UKPuzzleToy.php');
$britishToyFactory = new UKToyFactory();
var_dump($britishToyFactory->makeMaze());
echo "\n";
var_dump($britishToyFactory->makePuzzle());
echo "\n";
如果运行给定的代码,输出应类似于以下屏幕截图中所示的输出:

现在,假设我们想添加一个新的工厂(其中包含一组新的产品)(例如,纽约),我们只需添加玩具 NYMazeToy
和 NYPuzzleToy
,然后创建一个名为 NYToyFactory
的新工厂(实现 ToyFactory
接口),就大功告成了。
现在,当你需要添加新的 产品类 时,这个类的缺点就显现出来了;抽象工厂需要更新,这违反了接口隔离原则。因此,如果需要添加新的产品类,它并不严格符合 SOLID 原则。
这种设计模式可能需要一些时间才能完全理解,因此请务必仔细琢磨源代码,看看你能用它做些什么。