9 October 2017

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

ProgrammingSystem Analysis and DesignDesigning and refactoringC#ООP


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


Определение


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


Пример


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


Антипример


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


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


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


Назначение


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


Анализ


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


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


Создание


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


Модификация


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


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


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


Разработка


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


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


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


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


Сложность


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


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


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


Итоги


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


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


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

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


  1. Принцип разделение интерфейсов — единственная ответственность для контрактов
  2. Принцип открытости-закрытости — единственная ответственность для реализаций.
  3. Внедрение зависимостей — выделение композиции объектов как отдельной ответственности.
  4. Фабрика — выделение создания объектов
  5. ORM — выделение поддержки отображения объектов в базах данных
Tags:SRPSOLIDdesign patternssingletongod objectOOP
Hubs: Programming System Analysis and Design Designing and refactoring C# ООP
+3
13.5k 54
Comments 58
Popular right now
C++ Developer. Professional
December 28, 202060,000 ₽OTUS
Программирование на языке C (Си)
December 14, 202022,990 ₽Специалист.ру
C++ Junior Developer
March 3, 202123,990 ₽Level UP
Профессия Java-разработчик
December 1, 202082,500 ₽SkillFactory
Top of the last 24 hours