Pull to refresh

Принцип единственной ответственности: фундамент декомпозиции

Reading time4 min
Views16K


Сейчас об этом принципе слышал любой, кто занимается программированием. Чуть меньше тех, кто думает, что его знает. Гораздо меньше тех, кто действительно умеет его использовать. Я постараюсь объяснить суть, назначение и применение этого принципа как можно проще и короче.


Определение


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


Пример


Lazy<T> — обертка для объекта, чье создание откладывается до первого обращения к нему.


Антипример


Синглтон — класс, не допускающий создания более одного экземпляра. В этом описании нет союзов, но оно неполное — синглтон всегда имеет основную функциональность помимо контроля единственности собственного экземпляра. Синглтон — класс, реализующий полезную функциональность и контролирующий единственность собственного экземпляра. Теперь описание исчерпывающее, но имеет союз "и" — у синглтона два разных назначения. Он не соответствует принципу единственной ответственности.


Еще антипример


Локатор сервисов — позволяет получить доступ к любому сервису приложения. Это описание без исчерпывающего списка сервисов заведомо неполное.


Назначение


Упрощение создания, анализа и модификации программных систем.


Анализ


Анализ и оценка упрощаются за счет априорного знания ответа на вопрос "Зачем это нужно?".
Программный объект с единственной ответственностью будет заведомо проще и меньше, чем его визави с дополнительной нагрузкой. Напротив, объект с множеством ответственностей часто не позволяет дать исчерпывающий ответить на вопрос "Зачем это создали?" даже собственному автору. Традиционным аргументом против применения принципа единственной ответственности является большее число мелких объектов в проекте. В меньшем числе более крупных сущностей якобы проще ориентироваться и понимать структуру приложения в целом.


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


Создание


Создание новых и расширение существующих программных систем облегчается за счет упрощенного переиспользования. У объектов с единственной ответственностью по построению нет избыточной функциональности. В результате они либо нужны для реализации задач проекта целиком, либо не нужны совсем. Не надо поддерживать избыточные возможности. Не надо дублировать имеющиеся реализации из-за дороговизны предыдущего пункта.


Модификация


Модификация существующей функциональности удешевляется за счет лучшей локализации изменений. Мелкие изменения видны в системе контроля версий с точностью до конкретной ответственности. Крупные модификации заметны сразу за счет большого количества измененных файлов. Юнит-тесты для объектов с единственной ответственностью дают больше информации о внесенных в код дефектах.


Противопоказания


Единственное реальное противопоказание — оптимизация по потреблению ресурсов в ходе разработки и эксплуатации программных систем


Разработка


Стоимость первичной разработки с последовательным соблюдение принципа единственной ответственности выше за счет необходимости более тщательного анализа и увеличения объема кода в рамках конкретной задачи.


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


Эксплуатация


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


Сложность


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


Например, изучение функционального программирования позволяет замечать и выделять ответственности, которые очень сложно обнаружить, используя только ООП. Главным препятствием для использования принципа является его контринтуитивность: человеческий мозг склонен искать и находить единственный "простой" ответ на все сложные вопросы.


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


Итоги


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


Применение в .NET


  1. Интерфейсы — выделение контрактов как отдельной ответственности.
  2. Классы — выделение реализаций контактов.
  3. Методы — выделение алгоритмов.
  4. Делегаты — выделение полиморфизма.

Применение в принципах и паттернах


  1. Принцип разделение интерфейсов — единственная ответственность для контрактов
  2. Принцип открытости-закрытости — единственная ответственность для реализаций.
  3. Внедрение зависимостей — выделение композиции объектов как отдельной ответственности.
  4. Фабрика — выделение создания объектов
  5. ORM — выделение поддержки отображения объектов в базах данных
Tags:
Hubs:
+3
Comments58

Articles