Ответ
Factory Method (Фабричный метод) — это порождающий паттерн проектирования, который определяет интерфейс для создания объекта, но позволяет подклассам изменять тип создаваемого объекта. Он инкапсулирует процесс создания, делая систему независимой от конкретных классов продуктов.
Структура и участники:
- Product — интерфейс создаваемых объектов.
- ConcreteProduct — конкретные реализации Product.
- Creator — абстрактный класс, объявляющий фабричный метод
createProduct(). Он может содержать бизнес-логику, работающую с продуктами. - ConcreteCreator — переопределяет фабричный метод, чтобы возвращать конкретный экземпляр
ConcreteProduct.
Пример на Java:
// 1. Product (Интерфейс продукта)
interface Button {
void render();
void onClick();
}
// 2. Concrete Products (Конкретные продукты)
class WindowsButton implements Button {
public void render() { System.out.println("Render Windows-style button"); }
public void onClick() { System.out.println("Windows button clicked"); }
}
class HtmlButton implements Button {
public void render() { System.out.println("Render HTML button"); }
public void onClick() { System.out.println("HTML button clicked"); }
}
// 3. Creator (Создатель)
abstract class Dialog {
// ФАБРИЧНЫЙ МЕТОД (может быть абстрактным или иметь реализацию по умолчанию)
abstract Button createButton();
// Бизнес-логика, не зависящая от конкретного типа кнопки
public void renderWindow() {
Button okButton = createButton(); // Используем фабричный метод
okButton.render();
okButton.onClick();
}
}
// 4. Concrete Creators (Конкретные создатели)
class WindowsDialog extends Dialog {
@Override
Button createButton() {
return new WindowsButton(); // Создаем конкретную кнопку для Windows
}
}
class WebDialog extends Dialog {
@Override
Button createButton() {
return new HtmlButton(); // Создаем конкретную кнопку для Web
}
}
// Клиентский код
public class Application {
private Dialog dialog;
void initialize(String os) {
if (os.equals("Windows")) {
dialog = new WindowsDialog();
} else {
dialog = new WebDialog();
}
}
void main() {
initialize(System.getProperty("os.name"));
dialog.renderWindow(); // Будет создана и отрисована нужная кнопка
}
}
Преимущества:
- Избавляет от жесткой привязки к конкретным классам продуктов.
- Соблюдает принцип открытости/закрытости (OCP) — для добавления нового типа продукта нужно создать новый подкласс
Creator, не меняя существующий код. - Переносит код создания продуктов в одно место, упрощая поддержку.
- Упрощает юнит-тестирование, так как можно подменить фабричный метод.
Когда использовать:
- Когда заранее неизвестны типы и зависимости объектов, с которыми должен работать ваш код.
- Когда вы хотите предоставить возможность расширения вашей библиотеки или фреймворка.
- Когда нужно дать подклассам возможность выбирать создаваемый объект (как в примере с кнопками для разных ОС).
Ответ 18+ 🔞
Давай разберем эту вашу фабрику, блядь. Сидишь ты такой, пишешь код, и тут понимаешь — а я нихуя не знаю, какую кнопку создавать! WindowsButton или HtmlButton? А если завтра понадобится кнопка для холодильника, блядь? Пиздец, а не архитектура.
Вот тут-то и выплывает наш спаситель — Фабричный Метод. Суть его проста, как три копейки: есть у тебя некий начальник цеха (Creator), который говорит: «Ребята, я знаю, как работать с кнопками, но вот делать их — это не моя ебальная проблема. Пусть каждый цех (ConcreteCreator) делает свою».
Смотри, как это выглядит в жизни, без всей этой академической хуйни.
-
Продукт (Product) — это типа общий стандарт, контракт. Все кнопки должны уметь
render()иonClick(). Ну типа, любая кнопка, хоть деревянная, хоть нарисованная, должна отрисовываться и на неё можно тыкнуть. ИнтерфейсButton— это и есть тот самый указ свыше. -
Конкретная хрень (ConcreteProduct) — это уже реализация. Вот
WindowsButton— толстая, серая, с тенью, нажимается со звуком «клик». АHtmlButton— это просто див с бордером, который наonclickвызовет какую-нибудь javascript-функцию. Две абсолютно разные сущности, но обе — кнопки, ёпта! -
Создатель (Creator) — это главный по тарелочкам, абстрактный класс
Dialog. У него есть план, алгоритм, как работать с окном:renderWindow(). В этом плане сказано: «Чтобы отрендерить окно, мне нужна кнопка «ОК». Где её взять? А я, блядь, не знаю! Пусть мой наследник решает». И он объявляет абстрактный методcreateButton(). Это и есть фабричный метод, ебать его в сраку! Вся магия в том, что бизнес-логика (renderWindow) написана в терминах абстрактной кнопки, а не конкретной. -
Конкретный создатель (ConcreteCreator) — это уже специализированные цеха.
WindowsDialogзнает, что он в винде, и его фабричный методcreateButton()возвращаетnew WindowsButton().WebDialog— такой же патриот, но веба, и возвращаетnew HtmlButton(). Каждый сам решает, какую конкретную хрень производить.
И что в итоге? Клиентский код (Application) смотрит на операционку и говорит: «Ага, Windows. Значит, нужен WindowsDialog». Дальше он просто вызывает dialog.renderWindow() и похуй, что там внутри. А внутри WindowsDialog.renderWindow() вызовет свой фабричный метод, который создаст правильную кнопку. Всё, приехали.
Преимущества, блядь:
- Избавляет от жесткой привязки. Твой высокоуровневый код (
Dialog) не забит хуйней вродеnew WindowsButton(). Он работает с абстракцией. - Принцип открытости/закрытости (OCP) наше всё. Захотел кнопку для Android? Не лезешь в старый код с паяльником и криками «мама!». Просто создаёшь новый класс
AndroidDialogи в нём переопределяешьcreateButton(), чтобы он возвращалnew AndroidButton(). Старый код даже не чихнет. - Всё в одном месте. Если логика создания
WindowsButtonвдруг усложнится (ну там, нужно десять параметров в конструктор передать, блядь), ты меняешь её только в одном месте — в фабричном методеWindowsDialog. А не бегаешь по всему проекту, как угорелый. - Тестирование. Хочешь протестировать
Dialog? Подсуни ему в фабричный метод заглушку (MockButton), а не реальную кнопку. Красота, ёпта!
Когда это всё городить?
- Когда твой код должен работать с разными типами объектов, но ты на этапе написания этого кода нихуя не знаешь, с какими именно.
- Когда пишешь библиотеку или фреймворк и хочешь, чтобы другие распиздяи могли легко подключать свои собственные штуки, не переписывая твой код.
- Ну и как в примере — когда логика создания объекта зависит от контекста (ОС, тип UI, фаза луны, блядь).
Короче, паттерн как бы говорит: «Не спрашивай, откуда взять объект — просто запроси его, а система сама разберётся, какого хуя тебе подсунуть». Удобная штука, если не перепираться с ней.