Ответ
Паттерн Абстрактная фабрика (Abstract Factory) предоставляет интерфейс для создания семейств взаимосвязанных или взаимозависимых объектов без указания их конкретных классов. Он полезен, когда система должна быть независима от способа создания, компоновки и представления её объектов, или когда нужно создавать целые группы объектов, совместимых между собой.
Классический пример: Кросс-платформенный UI (GUI).
Допустим, мы пишем приложение, которое должно выглядеть нативно и на Windows, и на macOS. Кнопки и чекбоксы для этих систем реализованы по-разному.
- Определяем абстрактные продукты (интерфейсы для элементов UI):
// Абстрактная кнопка
public interface IButton
{
void Render();
void OnClick(Action action);
}
// Абстрактный чекбокс
public interface ICheckbox
{
void Render();
bool IsChecked { get; set; }
}
- Создаем конкретные продукты для каждой платформы:
// Реализация для Windows
public class WindowsButton : IButton
{
public void Render() => Console.WriteLine("Отрисована кнопка в стиле Windows");
public void OnClick(Action action) => Console.WriteLine("Нажатие обработано WinAPI");
}
public class WindowsCheckbox : ICheckbox
{
public bool IsChecked { get; set; }
public void Render() => Console.WriteLine($"[{(IsChecked ? 'X' : ' ')}] Чекбокс Windows");
}
// Реализация для macOS
public class MacButton : IButton
{
public void Render() => Console.WriteLine("Отрисована кнопка в стиле macOS");
public void OnClick(Action action) => Console.WriteLine("Нажатие обработано Cocoa");
}
public class MacCheckbox : ICheckbox
{
public bool IsChecked { get; set; }
public void Render() => Console.WriteLine($"[{(IsChecked ? '✓' : ' ')}] Чекбокс macOS");
}
- Определяем абстрактную фабрику (интерфейс для создания семейства):
public interface IUIFactory
{
IButton CreateButton();
ICheckbox CreateCheckbox();
}
- Реализуем конкретные фабрики для каждой платформы:
public class WindowsUIFactory : IUIFactory
{
public IButton CreateButton() => new WindowsButton();
public ICheckbox CreateCheckbox() => new WindowsCheckbox();
}
public class MacUIFactory : IUIFactory
{
public IButton CreateButton() => new MacButton();
public ICheckbox CreateCheckbox() => new MacCheckbox();
}
- Клиентский код работает только с абстракциями:
public class Application
{
private readonly IButton _button;
private readonly ICheckbox _checkbox;
// Приложение получает фабрику через конструктор (Dependency Injection)
public Application(IUIFactory factory)
{
_button = factory.CreateButton();
_checkbox = factory.CreateCheckbox();
}
public void RenderUI()
{
_button.Render();
_checkbox.Render();
}
}
- Использование: Конкретная фабрика выбирается на этапе конфигурации приложения (например, на основе операционной системы).
// Где-то в точке входа (Program.cs, Main)
IUIFactory factory;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
factory = new WindowsUIFactory();
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
factory = new MacUIFactory();
else
throw new PlatformNotSupportedException();
var app = new Application(factory);
app.RenderUI(); // Отрисует набор элементов для текущей ОС
Преимущества паттерна:
- Гарантирует совместимость объектов:
WindowsButtonвсегда будет создан вместе сWindowsCheckbox. - Изолирует конкретные классы: Клиентский код (
Application) не знает о существованииWindowsButtonилиMacButton. - Упрощает поддержку новых семейств: Чтобы добавить поддержку Linux, нужно создать
LinuxButton,LinuxCheckboxиLinuxUIFactory, не изменяя существующий клиентский код.
Отличие от Фабричного метода: Абстрактная фабрика создает семейство продуктов, а Фабричный метод — один продукт.
Ответ 18+ 🔞
Слушай, вот есть у тебя приложение, которое должно работать и на винде, и на маке. И кнопки с чекбоксами на этих системах выглядят по-разному, как будто их разные дизайнеры лепили, один — нормальный, а второй — ебанутый на всю голову. Так вот, чтобы не писать в коде кучу if (isWindows) ... else if (isMac) ... на каждом углу, умные дядьки придумали Абстрактную фабрику.
Суть в том, что ты создаёшь, блядь, целое семейство связанных объектов — например, весь интерфейс под одну операционку. И делаешь это через одну общую контору — фабрику.
Смотри, как это работает, на примере этого самого кросс-платформенного UI.
Шаг 1: Делаем абстрактные продукты. Это как чертежи для всех элементов.
// Абстрактная кнопка — что она вообще умеет?
public interface IButton
{
void Render();
void OnClick(Action action);
}
// Абстрактный чекбокс
public interface ICheckbox
{
void Render();
bool IsChecked { get; set; }
}
Пока что это просто интерфейсы, нихуя конкретного.
Шаг 2: Делаем конкретные реализации под каждую ОС. Вот тут уже пошла конкретика.
// Под Windows
public class WindowsButton : IButton
{
public void Render() => Console.WriteLine("Отрисована кнопка в стиле Windows");
public void OnClick(Action action) => Console.WriteLine("Нажатие обработано WinAPI");
}
public class WindowsCheckbox : ICheckbox
{
public bool IsChecked { get; set; }
public void Render() => Console.WriteLine($"[{(IsChecked ? 'X' : ' ')}] Чекбокс Windows");
}
// Под macOS
public class MacButton : IButton
{
public void Render() => Console.WriteLine("Отрисована кнопка в стиле macOS");
public void OnClick(Action action) => Console.WriteLine("Нажатие обработано Cocoa");
}
public class MacCheckbox : ICheckbox
{
public bool IsChecked { get; set; }
public void Render() => Console.WriteLine($"[{(IsChecked ? '✓' : ' ')}] Чекбокс macOS");
}
Видишь разницу? На винде галочка — это X, а на маке — ✓. Ну и отрисовка, ясное дело, разная.
Шаг 3: Создаём абстрактную фабрику. Это такая контора, которая умеет штамповать целый комплект — и кнопку, и чекбокс в одном стиле.
public interface IUIFactory
{
IButton CreateButton();
ICheckbox CreateCheckbox();
}
Шаг 4: Реализуем конкретные фабрики. Каждая фабрика знает, какие конкретные классы ей создавать.
public class WindowsUIFactory : IUIFactory
{
public IButton CreateButton() => new WindowsButton();
public ICheckbox CreateCheckbox() => new WindowsCheckbox();
}
public class MacUIFactory : IUIFactory
{
public IButton CreateButton() => new MacButton();
public ICheckbox CreateCheckbox() => new MacCheckbox();
}
WindowsUIFactory никогда, блядь, не создаст MacCheckbox. Гарантия совместимости на уровне компиляции — красота!
Шаг 5: Клиентский код. Он вообще не парится, под какую ОС он работает. Он работает с абстракциями.
public class Application
{
private readonly IButton _button;
private readonly ICheckbox _checkbox;
// Фабрику прокидываем извне (Dependency Injection, ёпта!)
public Application(IUIFactory factory)
{
_button = factory.CreateButton();
_checkbox = factory.CreateCheckbox();
}
public void RenderUI()
{
_button.Render();
_checkbox.Render();
}
}
Шаг 6: Использование. Всё решается в одном месте — при старте приложения.
// Где-то в начале, в Main
IUIFactory factory;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
factory = new WindowsUIFactory();
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
factory = new MacUIFactory();
else
throw new PlatformNotSupportedException("Твоя ОС — говно, извини");
var app = new Application(factory);
app.RenderUI(); // Автоматом отрисует правильный набор!
В чём, сука, профит?
- Совместимость: Ты точно не получишь виндовую кнопку с маковским чекбоксом. Фабрика следит, чтобы всё было из одного набора.
- Изоляция: Основной код приложения (
Application) нихуя не знает проWindowsButton. Он знает только про интерфейсIButton. Хоть завтра добавьLinuxButton— код приложения менять не придётся. - Масштабируемость: Захотел добавить поддержку Linux? Создаёшь
LinuxButton,LinuxCheckboxиLinuxUIFactory. Всё, пиздец. Существующий код даже не чихнёт.
Чем отличается от Фабричного метода? Да всё просто, блядь. Фабричный метод создаёт один вид продукта (например, только кнопки). Абстрактная фабрика создаёт целое семейство связанных продуктов (и кнопки, и чекбоксы, и скроллбары — всё в одном стиле). Если Фабричный метод — это цех по производству колёс, то Абстрактная фабрика — это целый завод, который делает и колёса, и двери, и рули для одной конкретной марки автомобиля.