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


Определение


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


Пример


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


Антипример


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


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


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


Назначение


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


Анализ


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


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


Создание


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


Модификация


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


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


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


Разработка


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


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


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


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


Сложность


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


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


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


Итоги


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


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


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

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


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

Комментарии (56)


  1. cranium256
    09.10.2017 09:19
    +6

    Если бы это был корпоративный блог, то было бы похоже, что компания решила проявить активность, что бы о ней не забывали.


  1. SirEdvin
    09.10.2017 10:18
    +5

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

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

    Аналогично можно говорить и про любое другое «синглтон, который делает то-то и то-то», так как обертка так же скрывает за собой кучу смысла, как и синглтон.


  1. redfs
    09.10.2017 10:38
    +3

    В связи с тем, что статья написана в стиле методического пособия, попробую немного изменить представление автора о синглтоне.

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

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

    На самом деле — синглтон прост в понимании, но не совсем прост в использовании. Дело в том, что синглтон предназначен для контроля единственности экземпляра любого класса. Собственно, в этом его основное предназначение и единственная ответственность.

    Все копья, которые ломаются вокруг синглтона, связаны в основном с частным (и часто
    — ошибочным) случаем его использования, когда он контролирует единственность своего экземпляра и имеет какую-то дополнительную функциональность. Но это проблема не синглтона, а понимания этого шаблона разработчиком и правильности его применения.


    1. Bonart Автор
      09.10.2017 13:16

      Дело в том, что синглтон предназначен для контроля единственности экземпляра любого класса.

      Для любого не получится.
      Например, класс не контролирует число своих экземпляров и имеет публичный конструктор.
      На всякий случай: я пользуюсь каноническим определением синглтона от банды четырех, именно в первую очередь находится поисковиками.
      Если вы пользуетесь другим, то прошу предоставить ссылку.
      Если вы готовы сделать сингтон для любого класса (не трогая его самого!) — предоставьте пожалуйста код.


      1. redfs
        09.10.2017 13:42

        Если вы готовы сделать сингтон для любого класса (не трогая его самого!) — предоставьте пожалуйста код.

        Хм… Да это вроде как должно быть понятно. Упрощенно, что-то типа
        class Single {
          static private $instance = null;
          private function __construct() {}
          static public function getInstance() {
            if ( self::$instance === null ) {
              self::$instance = new MyClass ();
            };
            return self::$instance;
          }
        }
        $myClassInstance = Single::getInstance();
        

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


        1. Bonart Автор
          09.10.2017 14:04

          Как именно ваш код не дает создать другие экземпляры класса MyClass?
          Что может помешать мне написать у себя new MyClass и получить второй объект?


          1. redfs
            09.10.2017 14:08

            Что может помешать мне написать у себя new MyClass и получить второй объект?
            Вот! Поэтому разработчик должен понимать, что он делает и для чего он это делает. А не на синглтон все косяки списывать :)


            1. Bonart Автор
              09.10.2017 14:11
              +1

              Значит то, что у вас получилось, не является синглтоном по вашему же определению.


              1. redfs
                09.10.2017 14:36

                Нет, не так.
                Это значит, что если у меня в проекте есть необходимость использовать несколько экземляров класса, то я не буду заворачивать его в синглтон. А если я хочу работать только с одним экземпляром (например, это может быть что-нибудь типа PDO), то подумаю над реализацией и синглтон кмк — не самая плохая из возможных.


                1. Bonart Автор
                  09.10.2017 14:46

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


                  1. redfs
                    09.10.2017 15:12

                    Вы как-то все с ног на голову повернули.
                    Вдумайтесь — я могу заставить весь свой код, который я контролирую, использовать синглтон обертку для класса, который я не контролирую. Разницу видите?


                    1. Bonart Автор
                      09.10.2017 15:21

                      Разницу видите?

                      Вижу. Эта разница и не дает считать вашу обертку синглтоном.


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

                      Ваша обертка таких гарантий не дает.
                      Это не значит, что она плохая.
                      Просто она не синглтон (по определению от банды четырех).


                      1. MacIn
                        09.10.2017 17:56

                        Это вопрос конкретного ЯП.
                        Публичный конструктор можно скрыть или запретить.


                        1. Bonart Автор
                          09.10.2017 19:18

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


                          1. redfs
                            09.10.2017 19:37

                            Только передергивать не надо. Я не говорил о своей реализации синглтона, а о реализации данного шаблона в рамках приложения и ошибочности фразы о «собственном экземпляре». Возможно, что употребил не самое правильное слово «любой» (как альтернативу слову «свой»), но вы ведь прекрасно поняли, о чем речь.


                          1. MacIn
                            09.10.2017 19:44

                            По-моему, нет, он говорил о своих исходниках, над которыми имеет контроль. Это вы пытаетесь задним числом добавить ограничений.


                            1. Bonart Автор
                              09.10.2017 19:56
                              +1

                              https://habrahabr.ru/post/338064/#comment_10462666


                              Дело в том, что синглтон предназначен для контроля единственности экземпляра любого класса.
                              Собственно, в этом его основное предназначение и единственная ответственность.

                              Слово "любого" выделено жирным, никаких "своих исходников".
                              Я использую общепринятое определение синглтона.
                              Далее, скрытие или запрещение конструктора означает, что класс начинает контролировать возможность создания своих экземпляров, т.е. берет на себя дополнительную ответственность.


                              1. MacIn
                                09.10.2017 20:20
                                +1

                                Простите, но это уже глупо. Если вести речь не о своих исходниках, а о закрытых 3rd party, как вы вообще можете гарантировать, что там что-то происходит вот так, а не иначе? Это определенно невозможно — вы не знаете, что есть в черном ящике чужого кода.
                                Следовательно, это толкование - заведомо неверное.

                                Я использую общепринятое определение синглтона.

                                Вы в статье использовали не общепринятое, а свое понимание, сузив шаблон проектирования до класса.

                                Далее, скрытие или запрещение конструктора означает, что класс начинает контролировать возможность создания своих экземпляров, т.е. берет на себя дополнительную ответственность.

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


                                1. Bonart Автор
                                  09.10.2017 20:43

                                  Если вести речь не о своих исходниках, а о закрытых 3rd party, как вы вообще можете гарантировать, что там что-то происходит вот так, а не иначе?

                                  Я никак не могу, но я и не пытался.


                                  Вы в статье использовали не общепринятое, а свое понимание, сузив шаблон проектирования до класса.

                                  Я не знаю способа реализации синглтона без нарушения SRP и полагаю что его не существует. Вы легко сможете опровергнуть этот тезис просто предъявив код. Пока то что я видел было либо не SRP либо не синглтон.


                                  Тогда у вас, по вашей логике, любой класс, имеющий приватные поля и методы имеет в качестве дополнительной отетственность по контролю за доступом

                                  Конечно, нет. Приватные члены вполне нормальны, ненормально отсуствие публичных.


                                  1. MacIn
                                    09.10.2017 20:49

                                    Я не знаю способа реализации синглтона без нарушения SRP и полагаю что его не существует. Вы легко сможете опровергнуть этот тезис просто предъявив код. Пока то что я видел было либо не SRP либо не синглтон.

                                    Потому что вы предъявляете необоснованные волюнтаристские ограничения.
                                    Конечно, нет. Приватные члены вполне нормальны, ненормально отсуствие публичных.

                                    Ну так есть у этого класса публичные методы — работать-то с ним надо. А конструктор — только приватный. И воспользоваться им может только кто-то friendly, второй класс, который вкупе с первым и образует синглтон.


                      1. redfs
                        09.10.2017 22:22

                        Просто она не синглтон (по определению от банды четырех).
                        Посмотрите пример на стр. 137. издательство Питер, 2016 и на код создания экземпляров классов BombedMazeFactory и EnhancedMazeFactory и других возможных подклассов
                        Может это и не синглтон, но только в вашем понимании, а никак не банды четырех.


                    1. sklavit
                      09.10.2017 20:15

                      Вы заимплементили фабрику, а не синглтон.


                      1. MacIn
                        09.10.2017 20:21

                        А кто сказал, что шаблон «синглтон» не может быть реализован посредством фабрики?


        1. Hardcoin
          09.10.2017 21:48

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


          Если подразумевать другую ответственность (например, только "представить глобальную точку доступа"), то все нормально. Хотя под общепринятое определение перестанет подходить.


      1. redfs
        09.10.2017 13:50

        Например, класс не контролирует число своих экземпляров и имеет публичный конструктор.
        Так именно для таких классов и используется синглтон, если возникает необходимость иметь только один экземпляр такого класса :)


      1. MacIn
        09.10.2017 17:53

        Еще как получится.
        Говорить, что синглтон — это «класс, не допускающий создания более одного экземпляра»(себя) — ошибочно.
        Синглтон — это шаблон проектирования, а не класс.
        Если у вас есть класс (или даже просто процедурной парадигмы функция), который единственный допускает создание второго класса, и только единожды, то эта пара будет тоже синглтоном. И у каждого класса будет только одна задача.


        1. Bonart Автор
          09.10.2017 20:00
          +1

          И у каждого класса будет только одна задача.

          Как вы предотвратите создания дополнительных экземпляров второго класса, не вмешиваясь в его код?
          Ведь у вас же уже есть первый класс, это его ответсвенность.


          1. MacIn
            09.10.2017 20:23

            А кто сказал, что мы не можем вмешиваться в его код, спроектировать его специальным образом, так, чтобы его нельзя было инстанцировать извне просто так, изначально?


            1. Bonart Автор
              09.10.2017 20:29
              +1

              В этом случае вы наложите на второй класс ограничения, не связанные с его функциональностью, т.е. возложите на него вторую ответственность.
              И первый ваш класс со своей задачей справиться сам не может, т.е. делит свою ответственность со вторым.
              Это нарушение SRP с особым цинизмом.


              1. MacIn
                09.10.2017 20:34

                Сокрытие методов не является возложением ответственности на класс. Существование объекта класса — непосредственно часть его функциональности.
                Иначе мы можем сказать, что наличие любого конструктора класса — это возложение на него не связанной с его функциональностью ответственности. Вот есть у нас класс String какой-нибудь. Его функциональность — хранение строкового типа данных и разные преобразования этих данных. А вот, мол, создание объекта такого класса с его функциональностью напрямую не связано…

                И второй ваш класс со своей задачей справиться сам не может, т.е. делит свою ответственность с первым.

                Может, но это ограничивается областью видимости.


                1. Bonart Автор
                  09.10.2017 20:47
                  +1

                  Сокрытие методов не является возложением ответственности на класс

                  Отсутствие публичного конструктора или его эквивалента — еще как является.
                  Сокрытие конструкторов не отвечает за исходную функциональность — оно для этого не нужно.
                  Это нужно только для реализации синглтона ЧТД.


                  1. MacIn
                    09.10.2017 20:55

                    Отсутствие публичного конструктора или его эквивалента — еще как является.

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

                    Сокрытие конструкторов не отвечает за исходную функциональность — оно для этого не нужно.

                    Ну так и сокрытие вот тех полей, о которых я написал выше, тоже не отвечает. Класс так же работал бы, будь они публичны.

                    Это нужно только для реализации синглтона ЧТД.

                    … а добавление в класс геттеров нужно только для получения этих данных извне, вот чудо-то.
                    Это ничего не меняет и не делает сокрытие конструктора побочной ответственностью; это деталь реализации.


  1. rraderio
    09.10.2017 11:35

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

    Контролировать единственность экземпляра может и компилятор а не только класс.
    docs.scala-lang.org/tour/singleton-objects.html


  1. andreyverbin
    09.10.2017 11:59
    +1

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


    Тут меня сразу подмывает задать несколько вопросов:
    — Назначение объекта типа «калькулятор» складывать или умножать?
    — Ограничения на длину предложения существуют?
    — Предъявите требования к точности и емкости определения. Например — «Объект реализует функции операционной системы» это ответственность?
    — Являются ли ответственностью объекта такие штуки
    • Возможность работать на i386, x86_64, ARM
    • Умение работать в многопоточной среде
    • Переносимость на другие ОС
    • Асимптотическая сложность операций


    1. Bonart Автор
      09.10.2017 15:01
      +1

      Назначение объекта типа «калькулятор» складывать или умножать?

      Назначение объекта "калькулятор" — делать расчеты в соответсвии с исходными данными, используя операции из списка.


      Ограничения на длину предложения существуют

      Я думаю, достаточно неформального "без фанатизма"


      «Объект реализует функции операционной системы» это ответственность

      Нет. Нужен список функций или ссылка на него.


      Возможность работать на i386, x86_64, ARM
      Умение работать в многопоточной среде

      Нет. Не указано, что такое "работать" для вашего объекта.


      Переносимость на другие ОС
      Асимптотическая сложность операций

      То же самое. Эти описания неполные.


      1. andreyverbin
        09.10.2017 18:14

        Назначение объекта «калькулятор» — делать расчеты в соответсвии с исходными данными, используя операции из списка.

        Если для калькулятора указать список операций, то это будет сильно больше чем «Его можно исчерпывающе описать одним предложением, не используя союзы.»
        Для ОС тоже можно указать список, там очень много пунктов. Я думаю никто не обрадуется увидев такой объект в ядре операционной системы. Тогда возникает вопрос — почему для калькулятора список функций это ок, а для ОС видимо не ок.

        Вы просите дать полное описание того, что такое «работать». Полные описания редко помещаются в одно простое предложение. Любой программный компонент имеет нефункциональные требования к производительности, портируемости работе в многопоточной среде.


        1. Bonart Автор
          09.10.2017 19:15

          Если для калькулятора указать список операций

          … то это должна быть ссылка. Или параметр конструктора.


          Полные описания редко помещаются в одно простое предложение.

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


          Любой программный компонент имеет нефункциональные требования

          С точки зрения ответственности нефункциональные требования в отрыве от функциональных не имеют смысла.


          1. andreyverbin
            11.10.2017 00:39

            … то это должна быть ссылка. Или параметр конструктора.

            1. Что с ОС делать предлагаете?
            2. Если «список операций» это зависимость, то что останется в калькуляторе?
            3. Почему то, что останется должно быть именно в калькуляторе, а не передаваться также в качестве зависимости?


  1. mkuzmin
    09.10.2017 13:32
    +1

    Этот принцип неправильно трактуют.


    У компонента должна быть одна причина для изменения.
    Причину формулирует роль на проекте.
    Есть роль DBA и роль Аналитик. Так вот они вместе не должны требовать изменения одного компонента.


    Посмотрите работы Роберта Мартина. Есть видео в clean coders, и книга Clean Architecture.


    1. Bonart Автор
      09.10.2017 14:16

      Я читал Роберта Мартина.
      Его исходное определение SRP эквивалентно использованному в статье, но на практике очень неудобно для объяснений.


      1. mkuzmin
        09.10.2017 14:21

        Цитата из последней книги Роберта Мартина: Clean Architecture.


        A function should do one, and only one, thing. We use that principle when we are refactoring large functions into smaller functions; we use it at the lowest levels. But it is not one of the SOLID principles—it is not the SRP.
        Historically, the SRP has been described this way: A module should have one, and only one, reason to change.
        Software systems are changed to satisfy users and stakeholders; those users and stakeholders are the “reason to change” that the principle is talking about. Indeed, we can rephrase the principle to say this: A module should be responsible to one, and only one, user or stakeholder.


        1. Bonart Автор
          09.10.2017 14:51

          Перефразированное определение намного уже исходного.


          1. mkuzmin
            09.10.2017 14:55

            Нужно весь раздел читать. Там есть примеры. По понятным причинам, я не могу его сюда вставить.


            1. Bonart Автор
              09.10.2017 15:18

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


  1. yar3333
    09.10.2017 13:48

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


    1. Bonart Автор
      10.10.2017 13:59

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

      Я придерживаюсь гораздо более жесткой позиции: пренебрегать критерием единственной ответственности можно только в одноразовом коде на выброс.


      нужно поработать на большом проекте, упереться в проблему сложности и начать рефакторинг — тогда станет понятно, что к чему

      Это очень дорогой способ. Лучше поработать под началом лида, владеющего SRP.


  1. gonzazoid
    09.10.2017 13:55
    +2

    mkuzmin У компонента должна быть одна причина для изменения. — Что влечет за собой атомарность функционала — два юнита в одной обертке дают две возможных причины для правки. Не знаю — спорю я с Вами или просто уточняю, не важно )

    Автор — синглтон становится антипаттерном не от структуры определения или порядка слов в нем а от сферы(практики) применения. Статичные свойства класса внезапно тоже синглтоны — и да, с ними могут быть связаны неприятные моменты, но это не делает их безоговорочно вне закона. В подобной дискуссии я уже как то приводил пример — перечисления в Java реализованы через синглтоны. Объекты window и document в js — синглтоны. Можно натянуть сову на глобус, поменять стандарт и заставить всех получать их через фабрику, сервайс локатор или иной кошерный паттерн но в любом раскладе будет возвращаться один и тот же объект — нафига сову страдать заставили?

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

    Массив — структура которая позволяет получить доступ к любому элементу по его индексу.

    Ехарный бабай, не зная того что и сколько лежит в массиве вы не поймете массив? Это делает его антипаттерном? Пипец блин, программисты уже как юристы стали. Мужики, есть вещи где нужно математически строгое определение а есть вещи где достаточно интуитивной понятности и не надо потеть переставляя слова в предложении. Паттерны — они для людей, это просто расширение профессионального словаря и пока мы не пишем систем автоматического построения архитектуры приложения (робот-программист, ага) — математически строгое определение не нужно. Да, быть гуманитарием стыдно и неудобно, но маленький гуманитарий в глубине нашей души — это то что отличает нас от роботов.


    1. Bonart Автор
      09.10.2017 14:41

      > Статичные свойства класса внезапно тоже синглтоны

      Синглтон требует не только единой точки доступа к экземпляру, так что одного статического свойства недостаточно.


      1. gonzazoid
        10.10.2017 17:27

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


    1. Bonart Автор
      09.10.2017 16:23
      -2

      > Объекты window и document в js — синглтоны

      Да, мало кто считает яваксрипт образцом соблюдения принципа единственной ответственности.

      > Массив — структура которая позволяет получить доступ к любому элементу по его индексу.

      Массив имеет известный тип элементов и известное их количество. Взятие элемента по индексу дает предсказуемый результат.
      Локатор сервисов позволяет запросить любой тип, но вернет только то что зарегистрировано, а иначе бросит исключение. А то, что именно зарегистрировано — деталь реализации. Как результат мы имеем контракт, у которого реальная ответственность отсутствует.


  1. lxsmkv
    09.10.2017 17:43

    редкий случай когда статья в Викпедии обьясняет тему понятней и подробнее, с доходчивыми примерами для широкого круга читателей.


    1. Bonart Автор
      09.10.2017 19:51

      Давайте проверим.


      Например, представьте себе класс, который составляет и печатает отчёт. Такой класс может измениться по двум причинам

      Для оригинального критерия из вики потребовались дополнительные рассуждения, хотя сразу видно, что составление отчета И его печать — две ответственности по построению.


      SRP говорит, что в таком случае нужно разделить класс на два новых класса, для которых будет характерна только одна ответственность.

      Делить надо на три класса — еще в одном будет пользовательский сценарий полного циксла печати отчета.


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

      Я не знаю такого критерия качества как "здоровье" класса.


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

      Это зависит от характера изменений, а не вероятности. Разумеется, в случае SPR контракт у печатающего класса будет свой и для него определить необходимость изменений станет проще. Но в вики об этом ни слова.


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

      Это просто шедевр полезности. Если SRP не нарушен, то его можно не применять.


      Слепое следование принципу единственной ответственности приводит к избыточной сложности приложения, его поддержки и тестированию

      Это дезинформация в чистом виде, причем без малейших намеков на аргументацию.


      Принцип SRP можно применить только в том случае, когда:
      объекту класса становится позволительно слишком много;
      доменная логика концентрируется только в одном классе;
      любое изменение логики поведения объекта приводит к изменениям в других местах приложения, где это не подразумевалось изначально;
      приходится тестировать, исправлять ошибки, компилировать различные места приложения, даже если за их работоспособность отвечает третья сторона;
      невозможно легко отделить и применить класс в другой сфере приложения, так как это потянет ненужные зависимости.

      Эти рекомендации несут прямой и явный вред.


      Объединение ответственностей является общепринятой практикой и в этом нет ничего плохого до тех пор пока это легко обслуживать

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


      Следование принципу единой ответственности зависит от функций программного продукта и является труднейшим при проектировании приложений.

      Неправда. Следовать принципу единственной ответственности просто (это не KISS с YAGNI), сложно выделять ответственности при проектировании.


      В некоторых случаях это утверждение спорно, так как сам по себе объект, реализующий ActiveRecord, не содержащий никакой бизнес логики, а предоставляющий таблицу из базы данных имеет лишь одну причину для изменения (изменение таблицы), что не противоречит определением принципа SRP

      Только вот ORM позволяет выделить из такого объекта ответственность за генерацию и пакетное выполнение запросов к базе.


      Итог: статья в вике — очень плохой источник сведений об SRP


      1. lxsmkv
        09.10.2017 21:43

        Спасибо Вам за подробный анализ. Думаю это хорошее дополнение к статье.
        Я не являюсь свитерато-бородатым програмистом в третьем колене, а просто интересуюсь базовыми принципами информатики, так, больше для общего развития. Поэтому для меня наглядность важнее формальной точности. И Википедия в этом плане к сожалению часто тоже не достаточно популярно излагает. С минимальной дидактической редукцией, так сказать.
        А сам принцип единственного предназначения (мне такое название больше нравится) довольно интуитивен для понимания: если выключатель в корридоре включает свет и в корридоре и в зале, нам это покажется, как минимум, странным. А создатель такого решения просто подумал, мол почему бы и нет, ведь он, когда приходит домой, сразу идет в зал к телевизору. Звучит вроде обоснованно, но на практике выглядит дико. А в зале есть выключатель который выключает свет в корридоре. (реальный случай из жизни, между прочим)
        Или более распространенный случай, но в обратную сторону, у нас два выключателя в корридоре, и оба включают и выключают свет. Тут можно сказать, что нарушается принцип неповторения (DRY), но это так же и случай нарушения единственного предназначения. На один функционал (свет в корридоре) должна быть одна _и только одна_ управляющая компонента (выключатель).
        Другой пример — бюрократия, каждое ведомство занимается только своим делом и ничем другим, и передает дело при необходимости другому ведомству. И не может быть двух ведомств имеющих одно и то же предназначение.
        Я надеялся в статье на такое вот наглядное описание, для чайников, без избыточного формализма.


        1. Bonart Автор
          10.10.2017 00:45

          если выключатель в корридоре включает свет и в корридоре и в зале, нам это покажется, как минимум, странным.

          Почему? Очень удобная реализация варианта использования "не хочу щелкать по всем выключателям".


          А в зале есть выключатель который выключает свет в корридоре.

          И это удобно.


          На один функционал (свет в корридоре) должна быть одна и только одна управляющая компонента (выключатель).

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


          И не может быть двух ведомств имеющих одно и то же предназначение.

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


          Я надеялся в статье на такое вот наглядное описание, для чайников, без избыточного формализма.

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


  1. VanquisherWinbringer
    09.10.2017 23:34

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


    1. Bonart Автор
      09.10.2017 23:46

      Все верно, только это паттерн DI (внедрение зависимостей), а не синглтон.