exam

Основные принципы проектирования ПО.

Основные принципы проектирования программного обеспечения

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

Группа принципов SOLID

SOLID — аббревиатура из пяти принципов объектно‑ориентированного проектирования (введены Робертом Мартином):

  1. SRP (Single Responsibility Principle) — принцип единственной ответственности
    • Каждый класс или модуль должен иметь только одну причину для изменения.
    • Пример: класс OrderProcessor не должен одновременно обрабатывать заказы и отправлять email‑уведомления. Лучше выделить отдельный класс NotificationService.
    • Результат: упрощение тестирования и сопровождения.
  2. OCP (Open/Closed Principle) — принцип открытости/закрытости
    • Программные сущности должны быть открыты для расширения, но закрыты для модификации.
    • Пример: чтобы добавить новый способ оплаты (криптовалюта), не нужно менять класс PaymentProcessor — достаточно создать новый класс, реализующий интерфейс PaymentMethod.
    • Результат: снижение риска ошибок при добавлении функционала.
  3. LSP (Liskov Substitution Principle) — принцип подстановки Барбары Лисков
    • Объекты подклассов должны заменять объекты базовых классов без нарушения корректности программы.
    • Пример: если класс Rectangle имеет методы setWidth() и setHeight(), то класс Square (наследник Rectangle) не должен менять поведение этих методов — иначе код, работающий с Rectangle, сломается.
    • Результат: предсказуемость поведения при наследовании.
  4. ISP (Interface Segregation Principle) — принцип разделения интерфейсов
    • Лучше иметь много специализированных интерфейсов, чем один универсальный.
    • Пример: вместо интерфейса Machine с методами print(), scan(), fax() создать отдельные интерфейсы Printer, Scanner, Fax. Классы реализуют только нужные им интерфейсы.
    • Результат: уменьшение ненужной зависимости.
  5. DIP (Dependency Inversion Principle) — принцип инверсии зависимостей
    • Зависимости должны строиться на абстракциях, а не на конкретных реализациях.
    • Пример: класс UserService зависит от интерфейса UserRepository, а не от класса MySQLUserRepository.
    • Результат: лёгкость замены реализаций (например, переход с MySQL на PostgreSQL).

Дополнительные ключевые принципы

  1. DRY (Don’t Repeat Yourself) — не повторяйся
    • Каждая часть знаний в системе должна иметь единственное представление.
    • Пример: вынести повторяющуюся логику валидации в отдельный сервис или утилиту.
    • Результат: упрощение поддержки и снижение ошибок.
  2. KISS (Keep It Simple, Stupid) — придерживайся простоты
    • Решения должны быть максимально простыми, насколько это возможно.
    • Пример: выбрать линейный поиск в массиве из 10 элементов вместо сложной хеш‑таблицы.
    • Результат: код легче читать, тестировать и изменять.
  3. YAGNI (You Ain’t Gonna Need It) — вам это не понадобится
    • Не реализуйте функционал, который не требуется прямо сейчас.
    • Пример: не добавлять поддержку 10 языков, если сейчас нужен только русский и английский.
    • Результат: сокращение объёма кода и времени разработки.
  4. Low Coupling / High Cohesion — низкая связанность / высокое сцепление
    • Низкая связанность: модули слабо зависят друг от друга.
    • Высокое сцепление: методы внутри класса тесно связаны по функционалу.
    • Пример: модуль авторизации не должен напрямую зависеть от модуля отчётов.
    • Результат: возможность менять один модуль без влияния на другие.
  5. POLA (Principle of Least Astonishment) — принцип наименьшего удивления
    • Система должна вести себя так, как ожидает пользователь или разработчик.
    • Пример: метод deleteUser() должен удалять пользователя, а не помечать его как неактивного.
    • Результат: интуитивность API и интерфейса.
  6. SLAP (Single Level of Abstraction Principle) — принцип единого уровня абстракции
    • Код в одной функции/методе должен работать на одном уровне абстракции.
    • Пример: в методе processOrder() не смешивать вызовы saveToDatabase() и низкоуровневые SQL‑запросы.
    • Результат: читаемость и понятность кода.
  7. BDUF (Big Design Up Front) vs. итеративное проектирование
    • BDUF: детальное проектирование до начала разработки (подходит для критичных систем).
    • Итеративное: проектирование по частям, параллельно с разработкой (гибкие методологии).
    • Выбор: зависит от проекта — стабильность требований, сроки, риски.
  8. APO (Avoid Premature Optimization) — избегай преждевременной оптимизации
    • Оптимизируй код только после доказательства, что это необходимо (профилирование).
    • Пример: сначала реализуй алгоритм сортировки пузырьком, если данных мало; оптимизируй до быстрой сортировки, только если это станет узким местом.
    • Результат: экономия времени на ранних этапах.

Принципы архитектурного уровня

  • Модульность: система делится на независимые модули с чёткими интерфейсами.
  • Масштабируемость: архитектура позволяет наращивать нагрузку без кардинальной переработки.
  • Тестируемость: компоненты спроектированы так, чтобы их можно было легко протестировать (например, через внедрение зависимостей).
  • Безопасность: защита данных и операций заложена на этапе проектирования.
  • Производительность: ключевые операции оптимизированы с учётом ожидаемой нагрузки.
  • Восстанавливаемость: система может восстановиться после сбоев с минимальными потерями.

Как применять принципы на практике

  1. Начинайте с KISS и YAGNI: проектируйте просто и только то, что нужно сейчас.
  2. Соблюдайте SRP и ISP: разбивайте функционал на мелкие, узкоспециализированные компоненты.
  3. Используйте абстракции (DIP): зависимости от интерфейсов упрощают замену реализаций.
  4. Проверяйте LSP: убедитесь, что наследники не нарушают поведение родителей.
  5. Следуйте OCP: расширяйте функционал через новые классы, а не правку старых.
  6. Избегайте дублирования (DRY): выносите общую логику в утилиты или сервисы.
  7. Контролируйте связанность (Low Coupling): минимизируйте прямые зависимости между модулями.

Важно: принципы — это не догмы. Их применение должно быть прагматичным и учитывать контекст проекта (сроки, бюджет, команду, требования).