Menu
Coddy logo textTech

Паттерн Factory

Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy — урок 72 из 91.

Паттерн Фабрика (Factory Pattern) — это порождающий паттерн проектирования, который делегирует создание объектов отдельному классу или методу. Вместо того чтобы использовать new напрямую во всем коде, вы просите фабрику создать объекты за вас.

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

<?php
interface Notification {
    public function send(string $message): string;
}

class EmailNotification implements Notification {
    public function send(string $message): string {
        return "Email: $message";
    }
}

class SmsNotification implements Notification {
    public function send(string $message): string {
        return "SMS: $message";
    }
}

Без фабрики вы бы разбросали инструкции if и вызовы new повсюду. Фабрика централизует эту логику:

<?php
class NotificationFactory {
    public static function create(string $type): Notification {
        return match($type) {
            'email' => new EmailNotification(),
            'sms' => new SmsNotification(),
            default => throw new InvalidArgumentException("Unknown type: $type")
        };
    }
}

$notification = NotificationFactory::create('email');
echo $notification->send('Hello!');

Вывод:

Email: Hello!

Ключевое преимущество заключается в том, что ваш код зависит от интерфейса Notification, а не от конкретных классов. Если позже вы добавите класс PushNotification, вам нужно будет обновить только фабрику. Весь код, использующий фабрику, автоматически получит доступ к новому типу без каких-либо изменений.

challenge icon

Задание

Легко

Давайте создадим систему генерации документов, используя паттерн «Фабрика» (Factory Pattern). Различные типы документов (PDF, HTML, Plain Text) имеют общий интерфейс, но производят разные выходные данные — это идеальный сценарий для централизации создания объектов в фабрике.

Вы организуете свой код в четырех файлах:

  • Document.php — Создайте интерфейс Document, который определяет контракт, которому должны следовать все типы документов. Он должен иметь один метод render(string $content): string, который принимает контент и возвращает отформатированный результат документа.
  • Documents.php — Подключите интерфейс Document и создайте три класса, реализующих его:
    • PdfDocument — его метод render() возвращает "PDF: $content"
    • HtmlDocument — его метод render() возвращает "<html>$content</html>"
    • TextDocument — его метод render() возвращает "TXT: $content"
  • DocumentFactory.php — Подключите файл Documents и создайте класс DocumentFactory со статическим методом create(string $type): Document. Этот метод должен использовать match для возврата соответствующего объекта документа в зависимости от типа:
    • "pdf" возвращает новый PdfDocument
    • "html" возвращает новый HtmlDocument
    • "text" возвращает новый TextDocument
    • Любой другой тип должен выбрасывать исключение InvalidArgumentException с сообщением "Unknown document type: $type"
  • main.php — Подключите файл DocumentFactory. Вы получите два входных значения: тип документа и контент для рендеринга.

    Используйте фабрику для создания соответствующего типа документа, затем вызовите его метод render() с контентом и выведите результат.

    На новой строке выведите "Factory benefit: Adding new types only requires updating the factory", чтобы подчеркнуть, почему этот паттерн ценен.

Паттерн «Фабрика» делает ваш код гибким — когда вам позже понадобится добавить MarkdownDocument, вы обновите только фабрику, в то время как весь существующий код продолжит работать без изменений.

Шпаргалка

Паттерн Фабрика (Factory Pattern) делегирует создание объектов отдельному классу или методу вместо прямого использования new во всем коде.

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

Пример с системой уведомлений:

<?php
interface Notification {
    public function send(string $message): string;
}

class EmailNotification implements Notification {
    public function send(string $message): string {
        return "Email: $message";
    }
}

class SmsNotification implements Notification {
    public function send(string $message): string {
        return "SMS: $message";
    }
}

Фабрика централизует логику создания объектов:

<?php
class NotificationFactory {
    public static function create(string $type): Notification {
        return match($type) {
            'email' => new EmailNotification(),
            'sms' => new SmsNotification(),
            default => throw new InvalidArgumentException("Unknown type: $type")
        };
    }
}

$notification = NotificationFactory::create('email');
echo $notification->send('Hello!'); // Output: Email: Hello!

Основные преимущества:

  • Код зависит от интерфейса, а не от конкретных классов
  • Добавление новых типов требует обновления только фабрики
  • Существующий код автоматически получает доступ к новым типам без модификации

Попробуйте сами

<?php
require_once 'DocumentFactory.php';

// Чтение входных данных
$type = trim(fgets(STDIN));
$content = trim(fgets(STDIN));

// TODO: Используйте DocumentFactory для создания соответствующего типа документа
// TODO: Вызовите метод render() с содержимым и выведите результат
// TODO: На новой строке выведите "Factory benefit: Adding new types only requires updating the factory"

?>
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование