Какие знаешь паттерны проектирования?

Ответ

Паттерны проектирования — это типовые, проверенные решения часто встречающихся проблем в архитектуре ПО. Их знание позволяет писать более гибкий, поддерживаемый и понятный код.

Основные категории и ключевые паттерны:

1. Порождающие (Creational)

Управляют процессом создания объектов.

  • Singleton: Гарантирует существование только одного экземпляра класса.
    public sealed class Logger
    {
        private static readonly Lazy<Logger> _instance = new Lazy<Logger>(() => new Logger());
        public static Logger Instance => _instance.Value;
        private Logger() { } // Приватный конструктор
        public void Log(string message) => Console.WriteLine($"[{DateTime.Now}] {message}");
    }
    // Использование: Logger.Instance.Log("Запуск приложения");
  • Factory Method: Определяет интерфейс для создания объекта, но позволяет подклассам изменять тип создаваемого объекта.
  • Abstract Factory: Создает семейства связанных объектов без указания их конкретных классов.

2. Структурные (Structural)

Отвечают за компоновку классов и объектов в более крупные структуры.

  • Adapter: Позволяет объектам с несовместимыми интерфейсами работать вместе. Например, адаптер для старой библиотеки логирования под новый интерфейс ILogger.
  • Composite: Позволяет сгруппировать объекты в древовидные структуры и работать с ними как с единым объектом (например, элементы UI: Panel, Button, TextBox).
  • Decorator: Динамически добавляет объекту новую функциональность, оборачивая его. Альтернатива наследованию.

3. Поведенческие (Behavioral)

Решают задачи эффективного взаимодействия и распределения ответственности между объектами.

  • Observer: Определяет зависимость "один-ко-многим" между объектами так, что при изменении состояния одного объекта все зависящие от него объекты уведомляются. Реализуется через события (event) в C#.
  • Strategy: Инкапсулирует алгоритм (стратегию) в отдельный класс, делая их взаимозаменяемыми.

    public interface ISortStrategy<T> { void Sort(List<T> list); }
    public class QuickSortStrategy<T> : ISortStrategy<T> { /* ... */ }
    public class MergeSortStrategy<T> : ISortStrategy<T> { /* ... */ }
    
    public class Sorter<T>
    {
        private ISortStrategy<T> _strategy;
        public void SetStrategy(ISortStrategy<T> strategy) => _strategy = strategy;
        public void ExecuteSort(List<T> list) => _strategy?.Sort(list);
    }
  • Command: Инкапсулирует запрос как объект, позволяя параметризовать клиентов с различными запросами, ставить запросы в очередь или логировать их.

Ответ 18+ 🔞

Слушай, а вот эти ваши паттерны проектирования — это ж, блядь, как готовые рецепты для программиста, понимаешь? Не надо каждый раз изобретать велосипед, когда можно взять проверенную схему и не накосячить. Это как кулинарная книга для кода: хочешь гибкости — открывай одну страницу, хочешь чтобы всё не развалилось — другую.

Ну, вот смотри, на какие три банды они делятся:

1. Порождающие (Creational)

Их задача — рожать объекты правильным образом, чтобы не получилось как в том анекдоте: «создал сто экземпляров, а они все в одной памяти шалят».

  • Singleton (Одиночка): Это когда на весь проект должен быть один-единственный экземпляр класса. Например, логгер, который везде один и тот же. Сделал его — и не надо по десять раз инициализировать, блядь.
    public sealed class Logger
    {
        private static readonly Lazy<Logger> _instance = new Lazy<Logger>(() => new Logger());
        public static Logger Instance => _instance.Value;
        private Logger() { } // Спрятал конструктор, чтобы левые люди не плодили
        public void Log(string message) => Console.WriteLine($"[{DateTime.Now}] {message}");
    }
    // Используется просто: Logger.Instance.Log("Приложение стартануло");

    Главное — не злоупотреблять, а то получится глобальная помойка, в которую все скидывают свои зависимости. Это пиздец как некрасиво.

  • Factory Method (Фабричный метод) и Abstract Factory (Абстрактная фабрика): Первый — это когда ты делегируешь создание объекта наследникам, а второй — когда нужно целое семейство связанных объектов собрать, не вдаваясь в подробности, кто там конкретно создаётся. Чистая магия, ёпта.

2. Структурные (Structural)

Тут речь о том, как склеить разные детали в одну большую и рабочую конструкцию, чтобы она не развалилась от первого чиха.

  • Adapter (Адаптер): Представь, у тебя есть новая крутая система, а подключать надо старую библиотеку, которая говорит на другом языке. Берёшь адаптер — и вуаля, они друг друга понимают. Как переходник с USB-C на старый разъём.
  • Composite (Компоновщик): Позволяет работать с группой объектов так, будто это один объект. Дерево файловой системы, интерфейс из кучи кнопок и панелей — везде он.
  • Decorator (Декоратор): Хочешь добавить функциональность объекту, но наследование — это слишком жирно и негибко? Оберни его в декоратор! Как лук в бургере: добавил слой — получил новый вкус, не переделывая всю котлету.

3. Поведенческие (Behavioral)

А вот это уже про то, как объекты между собой общаются и кто за что отвечает. Чтобы не было бардака, когда каждый лезет не в своё дело.

  • Observer (Наблюдатель): Классика! Один объект (издатель) кричит: «У меня данные изменились!», а куча других (подписчиков) тут же реагирует. В C# это нативные события (event), ими и пользуйся.
  • Strategy (Стратегия): Моя любимая, блядь. Суть: выносишь алгоритм в отдельный класс и подсовываешь его основному объекту. Хочешь быструю сортировку — вот тебе одна стратегия, хочешь стабильную — вот другая. Меняешь на лету, как перчатки.

    public interface ISortStrategy<T> { void Sort(List<T> list); }
    public class QuickSortStrategy<T> : ISortStrategy<T> { /* ... */ }
    public class MergeSortStrategy<T> : ISortStrategy<T> { /* ... */ }
    
    public class Sorter<T>
    {
        private ISortStrategy<T> _strategy;
        public void SetStrategy(ISortStrategy<T> strategy) => _strategy = strategy;
        public void ExecuteSort(List<T> list) => _strategy?.Sort(list);
    }

    Гибкость — просто овердохуичная.

  • Command (Команда): Заворачиваешь действие в объект. Зачем? А чтобы это действие можно было поставить в очередь, отложить на потом, отменить или записать в лог. Очень удобно для реализации всяких «Отменить/Повторить» в редакторах.

Короче, учи эти паттерны. Не чтобы везде их пихать, а чтобы понимать, каким инструментом какую проблему выебывать. Иначе будешь писать монолитный говнокод, который потом и сам не разберёшь.

Видео-ответы