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

в 1:03, , рубрики: C#, design patterns, god object, singleton, solid, srp, Анализ и проектирование систем, ооп, Программирование, Проектирование и рефакторинг

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

Определение

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

Пример

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

Антипример

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

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

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

Назначение

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

Анализ

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

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

Создание

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

Модификация

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

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

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

Разработка

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

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

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

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

Сложность

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

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

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

Итоги

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

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

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

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

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

Автор: Bonart

Источник

Поделиться

* - обязательные к заполнению поля