Для чего используется паттерн «Фабрика»?

Ответ

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

Основные цели:

  • Снижение связанности: Клиентский код зависит от абстракции (интерфейса/абстрактного класса), а не от конкретных классов создаваемых объектов.
  • Централизация и упрощение создания: Вся сложная логика инициализации (выбор конкретного класса, конфигурация) сосредоточена в одном месте.
  • Упрощение расширения: Для добавления нового типа продукта нужно изменить только код фабрики, а не все места в коде, где создаются объекты.

Пример на PHP:

// Абстракция продукта
interface Report {
    public function generate(): string;
}

// Конкретные продукты
class PdfReport implements Report {
    public function generate(): string {
        return 'PDF report content';
    }
}

class CsvReport implements Report {
    public function generate(): string {
        return 'CSV report content';
    }
}

// Создатель с фабричным методом
abstract class ReportCreator {
    // Фабричный метод
    abstract public function createReport(): Report;

    public function renderReport(): string {
        $report = $this->createReport(); // Создание делегируется подклассам
        return $report->generate();
    }
}

// Конкретные создатели
class PdfReportCreator extends ReportCreator {
    public function createReport(): Report {
        // Здесь может быть сложная логика инициализации PdfReport
        return new PdfReport();
    }
}

class CsvReportCreator extends ReportCreator {
    public function createReport(): Report {
        return new CsvReport();
    }
}

// Использование
$creator = new PdfReportCreator();
echo $creator->renderReport(); // Создаст и использует PdfReport

Таким образом, паттерн позволяет подклассам решать, объект какого конкретного класса создавать.

Ответ 18+ 🔞

Ну ты представляешь, эта твоя "Фабрика" — она как раз про то, чтобы не париться с этими вечными new на каждом углу. Вместо того чтобы в каждом месте кода, как обезьяна, тыкать new PdfReport() или new CsvReport(), ты эту поебень делегируешь специальному методу. И сидишь такой красавчик, код у тебя чистый.

Смотри, в чём прикол. Основная цель — снизить связанность, ёпта. То есть твоему основному коду, который отчёты рендерит, вообще похуй, какой там конкретный класс создаётся. Ему главное, чтобы объект был с методом generate(). А уж PdfReport это или CsvReport — это уже забота фабрики. Доверия ебать ноль к тем, кто будет этот код расширять, поэтому и прячешь логику создания.

Вторая фишка — централизация создания. Вся эта хитрая жопа с конфигурацией, выбором типа, инициализацией — она вся в одном месте, в этом фабричном методе. Не нужно по всему проекту искать, где ещё new CsvReport() захардкожен. Захотел поменять логику — пошёл в один класс и там всё сделал. Удобно же, правда?

Ну и третье — упрощение расширения. Захотел добавить, не знаю, ExcelReport — тебе не нужно перелопачивать триста файлов. Добавил новый класс отчёта и новый создатель для него. Всё, бизнес.

Пример на PHP, смотри:

// Вот это — контракт. Говорим: "Любой отчёт, мудила, должен уметь generate()"
interface Report {
    public function generate(): string;
}

// Конкретные реализации. Одна в PDF, другая в CSV. Делают одно и то же, но по-разному.
class PdfReport implements Report {
    public function generate(): string {
        return 'PDF report content';
    }
}

class CsvReport implements Report {
    public function generate(): string {
        return 'CSV report content';
    }
}

// А вот это — основатель нашего цеха. Класс-создатель.
abstract class ReportCreator {
    // А вот он, ёбаный фабричный метод! Самый сок. Абстрактный — значит, каждый подкласс будет решать сам.
    abstract public function createReport(): Report;

    // А это уже общая логика, которая использует то, что фабрика создаст.
    public function renderReport(): string {
        $report = $this->createReport(); // Магия! Не знаем что, но знаем что оно — Report.
        return $report->generate();
    }
}

// Конкретные начальники цехов. Каждый знает, какой отчёт ему штамповать.
class PdfReportCreator extends ReportCreator {
    public function createReport(): Report {
        // Тут, блядь, может быть овердохуища логики: настройки, зависимости, конфиги...
        return new PdfReport();
    }
}

class CsvReportCreator extends ReportCreator {
    public function createReport(): Report {
        // А тут может быть просто. Главное — контракт выполнить.
        return new CsvReport();
    }
}

// Использование. Красота!
$creator = new PdfReportCreator();
echo $creator->renderReport(); // Внутри создастся PdfReport, и всё сработает.

Короче, суть в том, что подклассы сами решают, какой объект создавать. Клиентский код (renderReport()) работает с абстракцией и ему абсолютно похуй на детали. Паттерн, конечно, не серебряная пуля, но когда у тебя логика создания начинает расползаться по проекту как тараканы — самое время задуматься о такой фабрике. Сам от себя охуеешь, насколько проще становится жить.