Что такое паттерн Фабрика (Factory Method)?

«Что такое паттерн Фабрика (Factory Method)?» — вопрос из категории Паттерны, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Суть: Класс делегирует ответственность по созданию экземпляров своим подклассам.

Пример на PHP (логирование в разные каналы):

// Продукт
interface Logger {
    public function log(string $message): void;
}

// Конкретные продукты
class FileLogger implements Logger {
    private string $filePath;

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

    public function log(string $message): void {
        file_put_contents($this->filePath, date('Y-m-d H:i:s') . ': ' . $message . PHP_EOL, FILE_APPEND);
    }
}

class DatabaseLogger implements Logger {
    private PDO $connection;

    public function __construct(PDO $connection) {
        $this->connection = $connection;
    }

    public function log(string $message): void {
        $stmt = $this->connection->prepare("INSERT INTO logs (message, created_at) VALUES (?, NOW())");
        $stmt->execute([$message]);
    }
}

// Создатель (Creator)
abstract class LoggerFactory {
    // Фабричный метод
    abstract public function createLogger(): Logger;

    // Бизнес-логика, использующая продукт
    public function logMessage(string $message): void {
        $logger = $this->createLogger();
        $logger->log($message);
        echo "Message logged via " . get_class($logger) . "n";
    }
}

// Конкретные создатели
class FileLoggerFactory extends LoggerFactory {
    private string $filePath;

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

    public function createLogger(): Logger {
        return new FileLogger($this->filePath);
    }
}

class DatabaseLoggerFactory extends LoggerFactory {
    private PDO $connection;

    public function __construct(PDO $connection) {
        $this->connection = $connection;
    }

    public function createLogger(): Logger {
        return new DatabaseLogger($this->connection);
    }
}

// Использование
// Клиентский код работает с фабрикой через абстрактный тип
function clientCode(LoggerFactory $factory) {
    $factory->logMessage("Application started");
}

// Конфигурация фабрики зависит от среды выполнения
if ($_ENV['LOG_CHANNEL'] === 'file') {
    $factory = new FileLoggerFactory('/var/log/app.log');
} else {
    $pdo = new PDO('mysql:host=localhost;dbname=app', 'user', 'pass');
    $factory = new DatabaseLoggerFactory($pdo);
}

clientCode($factory);

Преимущества:

  • Избегает жесткой привязки к конкретным классам продуктов в основном коде.
  • Принцип единой ответственности: Код создания продукта отделен от бизнес-логики.
  • Принцип открытости/закрытости: Легко ввести новые типы продуктов, создав новый подкласс создателя.

Отличие от Simple Factory: Factory Method использует наследование и полиморфизм, в то время как Simple Factory — это обычно один статический метод, который возвращает объект на основе входного параметра.