抽象工厂模式

首先,如果你在阅读本书之前读过一些背景资料,你一定听说过 "具体类" 这个词。这是什么意思呢?简单地说,它与抽象类正好相反;它是一个你可以实例化以创建对象的类。

抽象工厂由以下类组成:抽象工厂、具体工厂、抽象产品、具体产品和我们的客户端。

在工厂模式中,我们生产特定接口的实现(例如,notifier 是我们的接口,而 e-mailSMSpost 是我们的具体实现)。使用抽象工厂模式,我们将创建一个工厂接口的实现,每个工厂都知道如何创建自己的产品。

假设我们有两个玩具工厂,一个在旧金山,一个在伦敦。它们都知道如何在两个地点创建两家公司的产品。

有鉴于此,我们的 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";

如果运行给定的代码,输出应类似于以下屏幕截图中所示的输出:

image 2023 10 27 17 54 02 459

现在,假设我们想添加一个新的工厂(其中包含一组新的产品)(例如,纽约),我们只需添加玩具 NYMazeToyNYPuzzleToy,然后创建一个名为 NYToyFactory 的新工厂(实现 ToyFactory 接口),就大功告成了。

现在,当你需要添加新的 产品类 时,这个类的缺点就显现出来了;抽象工厂需要更新,这违反了接口隔离原则。因此,如果需要添加新的产品类,它并不严格符合 SOLID 原则。

这种设计模式可能需要一些时间才能完全理解,因此请务必仔细琢磨源代码,看看你能用它做些什么。