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

Ответ

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

1. Порождающие паттерны (Creational)

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

  • Singleton (Одиночка): Гарантирует, что у класса существует только один экземпляр, и предоставляет глобальную точку доступа к нему.
    public class Singleton {
        private static Singleton instance;
        private Singleton() {} // Приватный конструктор
        public static Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
  • Factory Method (Фабричный метод): Определяет интерфейс для создания объекта, но позволяет подклассам изменять тип создаваемого объекта.
  • Abstract Factory (Абстрактная фабрика): Создает семейства связанных объектов без указания их конкретных классов.
  • Builder (Строитель): Позволяет создавать сложные объекты пошагово, отделяя конструирование от представления.

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

Объясняют, как собирать объекты и классы в более крупные структуры, сохраняя гибкость и эффективность.

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

    class Coffee:
        def cost(self): return 5
    
    class MilkDecorator:
        def __init__(self, coffee):
            self._coffee = coffee
        def cost(self):
            return self._coffee.cost() + 2
    
    my_coffee = Coffee()
    my_coffee_with_milk = MilkDecorator(my_coffee)
    print(my_coffee_with_milk.cost()) # Вывод: 7
  • Facade (Фасад): Предоставляет простой интерфейс к сложной подсистеме.
  • Proxy (Заместитель): Является суррогатом или placeholder другого объекта для контроля доступа к нему.

3. Поведенческие паттерны (Behavioral)

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

  • Observer (Наблюдатель): Определяет зависимость «один-ко-многим» между объектами так, что при изменении состояния одного объекта все зависящие от него объекты уведомляются.
  • Strategy (Стратегия): Определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми.
  • Command (Команда): Инкапсулирует запрос как объект, позволяя параметризовать клиенты с различными запросами, ставить их в очередь или логировать.

Зачем это тестировщику?

Понимание паттернов помогает:

  1. Анализировать архитектуру: Быстрее разобраться в коде и логике приложения.
  2. Проектировать тесты: Предсказывать точки взаимодействия и потенциальные дефекты (например, состояние гонки в Singleton, неверная настройка Strategy).
  3. Общаться с разработчиками: Использовать общий словарь для обсуждения решений и проблем.

Ответ 18+ 🔞

А, паттерны проектирования, ёпта! Ну классика же, блядь! Это как вот эти типовые сценарии, когда ты в коде наступаешь на одни и те же грабли, а потом какой-то умный дядя придумал, как эти грабли, блядь, в ухо себе не засунуть, а красиво обойти. И записал это всё, чтобы мы, распиздяи, не изобретали велосипед с квадратными колёсами каждый раз.

Короче, их, этих паттернов, овердохуища, но все они по пизде разложены в три коробки, чтобы не путаться.

1. Порождающие (Creational) — они про то, как объекты на свет появляются

Типа, не просто new MyObject(), а с изяществом, блядь, с контролем. Чтобы не было, как в том анекдоте: «наплодили тут объектов, а кто за них отвечать будет?».

  • Singleton (Одиночка) — это когда ты такой: «Этому классу больше одного экземпляра на весь проект — ни-ни, нахуй!». Как главный календарь в компании — он один, блядь, и все к нему ходят.

    public class Singleton {
        private static Singleton instance; // Спрятали, сука, в статику
        private Singleton() {} // А конструктор закрыли, чтобы с улицы не лезли
        public static Singleton getInstance() {
            if (instance == null) { // Если ещё нету
                instance = new Singleton(); // Вот теперь создаём
            }
            return instance; // И всем одну и ту же штуку суём
        }
    }

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

  • Factory Method (Фабричный метод) — Ты говоришь системе: «Хочу транспорт!», а она тебе: «На чём поедем, сука? На машине или на танке?». И в зависимости от ответа — создаёт нужную хуйню. Не ты new вызываешь, а тебе её готовят.

  • Abstract Factory (Абстрактная фабрика) — Это уже целый завод, блядь. Не просто машину, а целый гарнитур: машину, водилу, дорогу и бензин одной марки. Семейство объектов, ёпта!

  • Builder (Строитель) — Когда объект такой сложный, что в конструктор 15 параметров передавать — пиздец. А тут ты по кирпичику его собираешь: house.addWall().addRoof().paint("green"). Красота!

2. Структурные (Structural) — про то, как объекты друг с другом сцепляются

Чтобы не получалась каша-малаша, а была конструкция, блядь. Как лего.

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

    class Coffee:
        def cost(self): return 5  # Просто кофе, скукота
    
    class MilkDecorator:
        def __init__(self, coffee):
            self._coffee = coffee  # Заворачиваем кофе в молоко
        def cost(self):
            return self._coffee.cost() + 2  # И накидываем ценник
    
    my_coffee = Coffee()
    my_coffee_with_milk = MilkDecorator(my_coffee)
    print(my_coffee_with_milk.cost()) # Вывод: 7
  • Facade (Фасад) — Это когда за кучей сложных классов, которые там друг друга ебут, делается один простой интерфейс. Типа «умный дом»: вместо того чтобы самому включать свет, кофеварку и музыку, ты жмёшь одну кнопку «Утро» — и фасад-объект внутри всё это дергает.
  • Proxy (Заместитель) — Суррогат, подставная хуйня. Объект-заглушка, который выглядит как настоящий, но может делать что-то своё: кэшировать вызовы, контролировать доступ (ну ты не зайдешь, пока не скажешь пароль), или лениво создавать тяжёлый объект только когда он реально понадобится.

3. Поведенческие (Behavioral) — про то, как объекты общаются и кто за что отвечает

Кто кому командует, кто за кем следит — вся эта кухня, блядь.

  • Observer (Наблюдатель) — Классика! Есть один главный объект (издатель), и куча подписчиков (наблюдателей). Как только у главного что-то меняется — он орет: «Эй, мудаки, я обновился!» — и все подписчики тут же бегут пересчитывать свои дела. Лента в соцсетях, ёпта — идеальный пример.
  • Strategy (Стратегия) — Есть задача, например, «добраться до работы». А способов (стратегий) — дохуя: на машине, на метро, на вертолёте. Ты можешь в рантайме подменить один способ на другой, не переписывая весь код поездки. Гибко, сука!
  • Command (Команда) — Ты инкапсулируешь какое-то действие (например, «включить свет») в отдельный объект-команду. Эту команду можно потом складывать в очередь, откладывать на потом, отменять или логировать. Как пульт от телевизора: кнопка — это объект команды, а не прямое проводное соединение с лампочкой.

Ну и нахуй это всё тестировщику?

А вот нахуй, блядь, не нахуй! Это ж твой скелет в шкафу, который надо знать.

  1. Чтобы не еб@ть мозг на разборках. Видишь в коде Singleton — сразу понимаешь, что состояние у него одно на всех потоки. И если тесты в параллель гонять — можно поймать глюки, которые в одиночном запуске не ловятся. Представляешь, какая находка для баг-репорта? «Ваш синглтон в условиях гонки ведёт себя как шлюха», — и разработчик тебе руку пожмёт, а не пошлёт нахуй.
  2. Чтобы тесты умные строить. Видишь Strategy — понимаешь, что надо проверить все стратегии по отдельности и чтобы их можно было подменить. Видишь Observer — пишешь тест, где отписываешься и смотришь, не приходят ли уведомления нахуй в пустоту. Это же прям карта местности, где мины закопаны!
  3. Чтобы на одном языке говорить. Приходишь к разработчику и говоришь: «Слушай, а тут у тебя фасад над этим модулем не проседает по перфомансу, когда все три сервиса дергает?». Он сразу понимает, о чём ты, а не думает, что ты про архитектурный декор здания спросил. Экономия времени и нервов — овердохуищная.

Короче, паттерны — это не академическая хуйня, а реальные инструменты, которые делают код предсказуемым. А предсказуемый код, блядь, — это рай для тестировщика. Ну, почти рай.