门面

Facade(又称 "门面")设计模式是一种奇特的东西;它们本质上是复杂系统的一个简单接口。Facade 设计模式的工作原理是提供一个单独的类,这个类本身会实例化其他类,并提供一个简单的接口来使用这些功能。

使用这种模式时需要注意的是,由于类是在 Facade 中实例化的,因此它所使用的类基本上是紧耦合的。在有些情况下,你希望这样,但在有些情况下,你不希望这样。在不需要这种行为的情况下,最好使用依赖注入。

我发现,在将一组差劲的应用程序接口封装成一个统一的应用程序接口时,这种方法非常有用。它可以减少外部依赖性,使复杂性内部化;这一过程可以使你的代码更具可读性。

我将用一个粗略的例子来演示这种模式,但这将有效地使该机制显而易见。

让我为玩具工厂提出三个类。

制造商(制造玩具的工厂)是一个简单的类,它的实例化可以决定一次要制造多少个玩具:

<?php

class Manufacturer
{
    private $capacity;

    public function __construct(int $capacity)
    {
        $this->capacity = $capacity;
    }

    public function build(): string
    {
        return uniqid();
    }
}

Post 类(运输快递)是一个从工厂发送玩具的简单函数:

<?php

class Post
{
    private $sender;

    public function __construct(string $sender)
    {
        $this->sender = $sender;
    }

    public function dispatch(string $item, string $to): bool
    {
        if (strlen($item) !== 13) {
            return false;
        }

        if (empty($to)) {
            return false;
        }

        return true;
    }
}

SMS 类通知客户他们的玩具已从工厂发货:

<?php

class SMS
{
    private $from;

    public function __construct(string $from)
    {
        $this->from = $from;
    }

    public function send(string $to, string $message): bool
    {
        if (empty($to)) {
            return false;
        }

        if (strlen($message) === 0) {
            return false;
        }

        echo $to . " received message: " . $message;
        return true;
    }
}

下面是我们的 ToyFactory 类,它充当 Facade 将所有这些类链接在一起并允许操作按顺序发生:

<?php
class ToyShop
{
    private $courier;
    private $manufacturer;
    private $sms;

    public function __construct(String $factoryAdress, String $contactNumber, int $capacity)
    {
        $this->courier = new Post($factoryAdress);
        $this->sms = new SMS($contactNumber);
        $this->manufacturer = new Manufacturer($capacity);
    }

    public function processOrder(string $address, $phone)
    {
        $item = $this->manufacturer->build();
        $this->courier->dispatch($item, $address);
        $this->sms->send($phone, "Your order has been shipped.");
    }
}

最后,我们可以将所有这些包装在我们的 index.php 文件中:

<?php
require_once('Manufacturer.php');
require_once('Post.php');
require_once('SMS.php');
require_once('ToyShop.php');

$childrensToyFactory = new ToyShop('1 Factory Lane, Oxfordshire', '07999999999', 5);
$childrensToyFactory->processOrder('8 Midsummer Boulevard', '07123456789');

运行此代码后,我们会看到来自 SMS 类的消息,显示文本消息已发送:

image 2023 10 30 18 38 56 321

在其他情况下,如果各个类是松散耦合在一起的,我们可能会发现使用依赖注入会更好。通过向 ToyFactory 类注入执行各种操作的对象,我们可以注入 ToyFactory 类可以操作的假类,从而使测试变得更容易。

就我个人而言,我非常希望代码尽可能易于测试,因此我不喜欢这种方法。

总结

本章通过介绍结构设计模式,扩展了我们在上一章开始学习的设计模式。

为此,我们学习了一些关键模式,以简化软件设计过程;这些模式确定了实现不同实体间关系的简单方法:

  • 我们学习了装饰器(Decorator),了解了如何对类进行封装以添加额外的行为,更重要的是,我们了解了这如何帮助我们遵守 "单一责任原则"(Single Responsibility Principle)。

  • 我们了解了类适配器和对象适配器,以及它们之间的区别。这里的关键收获是我们为什么会选择组合而不是继承的理由。

  • 我们回顾了 FlyWeight 设计模式,它可以帮助我们以节省内存的方式执行某些流程。

  • 我们了解了 Composite 设计模式如何帮助我们将对象的组合与单个对象同等对待。

  • 我们还学习了桥接设计模式,它可以让我们将抽象与实现分离开来,使两者独立变化。

  • 我们还学习了代理设计模式如何充当另一个类的接口,以及如何将其用作转发代理。

  • 最后,我们了解了如何使用 Facade 设计模式为复杂的系统提供一个简单的接口。

在下一章中,我们将通过讨论行为模式来结束设计模式部分,并准备讨论架构模式。