Содержание

Фасад (Facade)

Фасад — это структурный паттерн, который предоставляет простой (но урезанный) интерфейс к сложной системе объектов, библиотеке или фреймворку.

Кроме того, что Фасад позволяет снизить общую сложность программы, он также помогает вынести код, зависимый от внешней системы в единственное место.

Особенности паттерна на PHP

Применимость: Паттерн часто встречается в клиентских приложениях, написанных на PHP, которые используют классы-фасады для упрощения работы со сложными библиотеки или API.
\\ Признаки применения паттерна: Фасад угадывается в классе, который имеет простой интерфейс, но делегирует основную часть работы другим классам. Чаще всего, фасады сами следят за жизненным циклом объектов сложной системы.

пример

Этот пример показывает структуру паттерна Фасад, а именно — из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.

class Db {
 
    private $mysqli;
    private $host;
    private $user;
    private $pass;
    private $db;
 
    public function __construct($host, $user, $pass , $db) {
        $this->host = $host;
        $this->user = $user;
        $this->pass = $pass;
        $this->db = $db;
        $this->mysqli = null;
    }
 
    public function connect() {
        $this->mysqli = new \mysqli($this->host, $this->user, $this->pass,  $this->db);
 
        if ($this->mysqli->connect_error) {
            die('Ошибка подключения (' . $this->mysqli->connect_errno . ') '
                    . $this->mysqli->connect_error);
        }
    }
 
    public function saveDocument($message) 
    {
        $this->mysqli->query("INSERT INTO `messages` (text) VALUES ('".$message."')");
    }
 
}
 
class Mail {
 
    private $to;
    private $subject;
    private $message;
    private $headers;
 
    public function __construct($to, $subject, $message, $headers)
    {
        $this->to = $to;
        $this->subject = $subject;
        $this->message = $message;
        $this->headers = $headers;
    }
 
    public function sendMessage($message)
    {
        mail($this->to, $this->subject, $this->message, $this->headers);
    }
}
 
class Log 
{
    private $filePath;
 
    public function __construct($filePath) 
    {
        $this->filepath = $filePath;
    }
 
    public function addLog($message) 
    {
        file_put_contents($this->filepath, $message.PHP_EOL, FILE_APPEND);
    }
}
 
 
class Document 
{
    private $db;
    private $log;
    private $mail;
 
 
    public function __construct(Db $db, Log $log, Mail $mail) 
    {
        $this->db = $db;
        $this->log = $log;
        $this->mail= $mail;
    }
 
    public function save($message) {
 
        $this->db->connect();
        $this->db->saveDocument($message);
        $this->log->addLog($message);
        $this->mail->sendMessage($message);
 
    }
}
 
$db = new Db('localhost','root','','patterns');
$log = new Log('logFile.txt');
$mail = new Mail('admin@admin.com','subject','message','');
 
$document = new Document($db, $log, $mail);
$document->save("new document");

Результат выполнения

bd:



logFile.txt

new document

Пример из реальной жизни

В этом примере Фасад скрывает сложность API YouTube и библиотеки FFmpeg от клиентского кода. Вместо того, чтобы работать с десятками классов, клиент использует простой метод Фасада.

<?php
 
namespace RefactoringGuru\Facade\RealWorld;
 
/**
 * Фасад предоставляет единый метод загрузки видео с YouTube. Этот метод
 * скрывает всю сложность сетевого уровня PHP, API YouTube и библиотеки
 * преобразования видео (FFmpeg).
 */
class YouTubeDownloader
{
    protected $youtube;
    protected $ffmpeg;
 
    /**
     * Бывает удобным сделать Фасад ответственным за управление жизненным циклом
     * используемой подсистемы.
     */
    public function __construct(string $youtubeApiKey)
    {
        $this->youtube = new YouTube($youtubeApiKey);
        $this->ffmpeg = new FFMpeg();
    }
 
    /**
     * Фасад предоставляет простой метод загрузки видео и кодирования его в
     * целевой формат (для простоты понимания примера реальный код
     * закомментирован).
     */
    public function downloadVideo(string $url): void
    {
        echo "Fetching video metadata from youtube...\n";
        // $title = $this->youtube->fetchVideo($url)->getTitle();
        echo "Saving video file to a temporary file...\n";
        // $this->youtube->saveAs($url, "video.mpg");
 
        echo "Processing source video...\n";
        // $video = $this->ffmpeg->open('video.mpg');
        echo "Normalizing and resizing the video to smaller dimensions...\n";
        // $video
        //     ->filters()
        //     ->resize(new FFMpeg\Coordinate\Dimension(320, 240))
        //     ->synchronize();
        echo "Capturing preview image...\n";
        // $video
        //     ->frame(FFMpeg\Coordinate\TimeCode::fromSeconds(10))
        //     ->save($title . 'frame.jpg');
        echo "Saving video in target formats...\n";
        // $video
        //     ->save(new FFMpeg\Format\Video\X264(), $title . '.mp4')
        //     ->save(new FFMpeg\Format\Video\WMV(), $title . '.wmv')
        //     ->save(new FFMpeg\Format\Video\WebM(), $title . '.webm');
        echo "Done!\n";
    }
}
 
/**
 * Подсистема API YouTube.
 */
class YouTube
{
    public function fetchVideo(): string { /* ... */ }
 
    public function saveAs(string $path): void { /* ... */ }
 
    // ...дополнительные методы и классы...
}
 
/**
 * Подсистема FFmpeg (сложная библиотека работы с видео/аудио).
 */
class FFMpeg
{
    public static function create(): FFMpeg { /* ... */ }
 
    public function open(string $video): void { /* ... */ }
 
    // ...more methods and classes... RU: ...дополнительные методы и классы...
}
 
class FFMpegVideo
{
    public function filters(): self { /* ... */ }
 
    public function resize(): self { /* ... */ }
 
    public function synchronize(): self { /* ... */ }
 
    public function frame(): self { /* ... */ }
 
    public function save(string $path): self { /* ... */ }
 
    // ...more methods and classes... RU: ...дополнительные методы и классы...
}
 
 
/**
 * Клиентский код не зависит от классов подсистем. Любые изменения внутри кода
 * подсистем не будут влиять на клиентский код. Вам нужно будет всего лишь
 * обновить Фасад.
 */
function clientCode(YouTubeDownloader $facade)
{
    // ...
 
    $facade->downloadVideo("https://www.youtube.com/watch?v=QH2-TGUlwu4");
 
    // ...
}
 
$facade = new YouTubeDownloader("APIKEY-XXXXXXXXX");
clientCode($facade);

Output.txt: Результат выполнения

Fetching video metadata from youtube...
Saving video file to a temporary file...
Processing source video...
Normalizing and resizing the video to smaller dimensions...
Capturing preview image...
Saving video in target formats...
Done!