Какие паттерны проектирования (design patterns) вы знаете и в каких случаях они применяются?

«Какие паттерны проектирования (design patterns) вы знаете и в каких случаях они применяются?» — вопрос из категории Основы программирования, который задают на 10% собеседований QA Тестировщик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

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

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

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

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

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

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

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

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

  • Observer (Наблюдатель): Определяет зависимость "один-ко-многим" между объектами так, что при изменении состояния одного объекта все зависящие от него объекты уведомляются.
    • Применение: Системы событий (event-driven), MVC (модель уведомляет представления).
  • Strategy (Стратегия): Определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми.
    • Применение: Выбор алгоритма сортировки, способа оплаты (карта, PayPal), алгоритма валидации в runtime.

Для тестирования особенно полезны:

  • Factory и Builder — для создания сложных тестовых данных и объектов-заглушек.
  • Proxy — для создания моков (mock objects) и перехвата вызовов в unit-тестах.