Ответ
Фабричный метод — это порождающий паттерн, который определяет интерфейс (абстрактный метод) для создания объекта в базовом классе, но позволяет подклассам изменять тип создаваемого объекта. Таким образом, логика работы с продуктом остается в базовом классе, а конкретный класс продукта определяется наследниками.
Зачем он нужен? Чтобы отвязать основной код от конкретных классов создаваемых объектов. Это особенно полезно, когда у вас есть общая структура (виджет, диалог, обработчик), но конечная реализация может различаться в зависимости от контекста (платформа, настройки пользователя).
Пример на Dart/Flutter: Создаем диалоговое окно, внешний вид которого (кнопки) зависит от платформы.
// Абстрактный класс продукта - кнопка
abstract class DialogButton {
String get label;
void onPressed();
Widget render(); // Возвращает Flutter-виджет
}
// Конкретные продукты
class MaterialDialogButton implements DialogButton {
@override
final String label;
final VoidCallback onPressed;
MaterialDialogButton({required this.label, required this.onPressed});
@override
Widget render() {
return ElevatedButton(
onPressed: onPressed,
child: Text(label),
);
}
}
class CupertinoDialogButton implements DialogButton {
@override
final String label;
final VoidCallback onPressed;
CupertinoDialogButton({required this.label, required this.onPressed});
@override
Widget render() {
return CupertinoButton(
onPressed: onPressed,
child: Text(label),
);
}
}
// Базовый класс Creator с фабричным методом
abstract class AlertDialog {
// Фабричный метод - subclasses должны его реализовать
DialogButton createConfirmButton();
DialogButton createCancelButton();
// Общая логика построения диалога, использующая фабричный метод
Widget build(BuildContext context) {
final confirmButton = createConfirmButton();
final cancelButton = createCancelButton();
// Здесь общая логика компоновки виджетов диалога...
return Column(
mainAxisSize: MainAxisSize.min,
children: [
// ... содержимое диалога ...
Row(
children: [
cancelButton.render(),
confirmButton.render(),
],
),
],
);
}
}
// Конкретные создатели (Concrete Creators)
class MaterialAlertDialog extends AlertDialog {
final VoidCallback onConfirm;
final VoidCallback onCancel;
MaterialAlertDialog({required this.onConfirm, required this.onCancel});
@override
DialogButton createConfirmButton() {
return MaterialDialogButton(
label: 'OK',
onPressed: onConfirm,
);
}
@override
DialogButton createCancelButton() {
return MaterialDialogButton(
label: 'Cancel',
onPressed: onCancel,
);
}
}
class CupertinoAlertDialog extends AlertDialog {
final VoidCallback onConfirm;
final VoidCallback onCancel;
CupertinoAlertDialog({required this.onConfirm, required this.onCancel});
@override
DialogButton createConfirmButton() {
return CupertinoDialogButton(
label: 'OK',
onPressed: onConfirm,
);
}
@override
DialogButton createCancelButton() {
return CupertinoDialogButton(
label: 'Cancel',
onPressed: onCancel,
);
}
}
// Использование:
// Widget build(BuildContext context) {
// final dialog = Platform.isIOS
// ? CupertinoAlertDialog(onConfirm: _save, onCancel: _close)
// : MaterialAlertDialog(onConfirm: _save, onCancel: _close);
// return dialog.build(context);
// }
Преимущества:
- Избавляет от жесткой привязки к конкретным классам продуктов в основном коде.
- Принцип единой ответственности: код создания продукта переносится в одно место (подклассы).
- Принцип открытости/закрытости: легко добавить новый тип продукта, создав новый подкласс, не трогая существующий код.
Отличие от Абстрактной фабрики: Фабричный метод создает один тип продукта, а Абстрактная фабрика — целое семейство связанных продуктов.
Ответ 18+ 🔞
Давай разберем эту тему, чтобы в голове не осталось никакой каши. Фабричный метод — это, по сути, такая хитрая жопа в коде, которая говорит: «Э, сабака сука, не лезь ко мне с конкретными классами, я сам решу, какую штуку тебе отдать».
Если по-простому: есть у тебя базовый класс. В нём есть главный метод, который говорит: «Слушай, чувак, я тут всю основную работу сделаю, но конкретную деталь — кнопку там, или зверюгу какую — ты, наследничек, сделай сам». И ты в наследниках просто пишешь, какую именно кнопку создавать. А основная логика, где эта кнопка используется, уже готова и её не трогаешь. Волнение ебать, как удобно.
Зачем это всё? Чтобы твой основной код не превращался в свалку из if (platform.isIOS) ... else if (platform.isAndroid) .... Ты просто говоришь: «Создай мне диалог», а система сама, через фабричный метод, подсовывает нужные виджеты под платформу. Доверия ебать ноль к таким жестким связям, поэтому и придумали этот паттерн.
Смотри на примере, тут всё понятно станет. Делаем кнопки для диалога:
// Абстрактная кнопка — что она умеет делать
abstract class DialogButton {
String get label;
void onPressed();
Widget render();
}
// Конкретная кнопка для Android-стиля (Material)
class MaterialDialogButton implements DialogButton {
@override
final String label;
final VoidCallback onPressed;
MaterialDialogButton({required this.label, required this.onPressed});
@override
Widget render() {
return ElevatedButton(
onPressed: onPressed,
child: Text(label),
);
}
}
// Конкретная кнопка для iOS-стиля (Cupertino)
class CupertinoDialogButton implements DialogButton {
@override
final String label;
final VoidCallback onPressed;
CupertinoDialogButton({required this.label, required this.onPressed});
@override
Widget render() {
return CupertinoButton(
onPressed: onPressed,
child: Text(label),
);
}
}
А теперь самое вкусное — создатель. Он знает КАК построить диалог, но не знает ИЗ ЧЕГО.
// Базовый класс для диалога. Он — главный постройщик.
abstract class AlertDialog {
// Вот он, фабричный метод! Конкретной реализации тут нет.
// Это как договорённость: "Наследник, ты обязан мне кнопки предоставить".
DialogButton createConfirmButton();
DialogButton createCancelButton();
// А тут общая логика, которая использует эти абстрактные кнопки
Widget build(BuildContext context) {
final confirmButton = createConfirmButton(); // Создаётся где-то там, в наследнике
final cancelButton = createCancelButton();
return Column(
mainAxisSize: MainAxisSize.min,
children: [
// ... какой-то контент диалога ...
Row(
children: [
cancelButton.render(), // Используем как обычные виджеты
confirmButton.render(),
],
),
],
);
}
}
И вот появляются конкретные ребята, которые знают, какие кнопки лепить:
// Диалог для Material (Android)
class MaterialAlertDialog extends AlertDialog {
final VoidCallback onConfirm;
final VoidCallback onCancel;
MaterialAlertDialog({required this.onConfirm, required this.onCancel});
@override
DialogButton createConfirmButton() {
// А вот и конкретика! Возвращаем Material-кнопку.
return MaterialDialogButton(label: 'OK', onPressed: onConfirm);
}
@override
DialogButton createCancelButton() {
return MaterialDialogButton(label: 'Cancel', onPressed: onCancel);
}
}
// Диалог для Cupertino (iOS)
class CupertinoAlertDialog extends AlertDialog {
final VoidCallback onConfirm;
final VoidCallback onCancel;
CupertinoAlertDialog({required this.onConfirm, required this.onCancel});
@override
DialogButton createConfirmButton() {
// А тут уже Cupertino-кнопка. Логика та же, продукт — другой.
return CupertinoDialogButton(label: 'OK', onPressed: onConfirm);
}
@override
DialogButton createCancelButton() {
return CupertinoDialogButton(label: 'Cancel', onPressed: onCancel);
}
}
Как этим пользоваться? Да проще пареной репы!
Widget build(BuildContext context) {
// Выбираем, какую фабрику (наследника) использовать
final dialog = Platform.isIOS
? CupertinoAlertDialog(onConfirm: _save, onCancel: _close)
: MaterialAlertDialog(onConfirm: _save, onCancel: _close);
// Всё! Остальная логика уже внутри.
return dialog.build(context);
}
В чём соль, блядь?
- Отвязка намерения от реализации: Твой главный код не забит проверками платформ. Он просто работает с абстрактной кнопкой. Ёперный театр, а не архитектура!
- Всё по полочкам: Код создания кнопки живёт не где попало, а в своём уютном классе-наследнике.
- Расширяемость — овердохуища: Захотел добавить диалог для веба? Создал новый
WebAlertDialog, который возвращаетHtmlDialogButton. Основной кодAlertDialog.build()даже не чихнул. Ни хуя себе, правда?
Чем не является? Это НЕ Абстрактная фабрика. Фабричный метод — он про один продукт (кнопку). А Абстрактная фабрика — это когда у тебя целый набор связанных продуктов (кнопка, ползунок, чекбокс) для целой темы. Не путай, а то будет тебе хиросима и нигерсраки в архитектуре.