Disclaimer: Всем можно, ну а я чем хуже!?

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

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

S — Принцип единственной ответственности (Single Responsibility Principle или SRP)

Должна быть одна и только одна причина для изменения класса («A class should have only one reason to change.» Robert C. Martin.)
Представим, что у вас на компе лежит пяток любимых песен, пара фильмов и фотки котиков. Вы валите все это в «Мои документы» и, в целом, радуетесь жизни.

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

Музыка
Фильмы
Котики

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

Музыка
  The Doors
    Waiting for the Sun
      Hello, I Love You.mp3
      ...
    ...
  RHCP
  Агутин
Видео
  Сериалы
  Фильмы
  Студия Private
Фото
  Котики
  Рыбалка
  Преферанс на даче
  куртизанки

И что мы тут имеем?

  • Музыка — отвечает ТОЛЬКО за музыку.
  • The Doors — отвечает ТОЛЬКО за музыку, сыгранную группой The Doors
  • Waiting for the Sun — отвечает ТОЛЬКО за альбом Waiting for the Sun
  • Hello, I Love You.mp3 — отвечает ТОЛЬКО за песню Hello, I Love You

Какая может быть причина изменения «The Doors»? Только одна — (качественное или количественное) изменение песен группы The Doors. А вот удаление надоевшего сериала из "/Видео/Сериалы/" такой причиной никак не может являться, так-же как и переименование «Музыка» в «Music».

Какие можно сделать выводы?

  1. SRP — это про декомпозицию (раскладываем по подпапочкам) и связность («Hello, I Love You.mp3» связана только с «Waiting for the Sun» и ей плевать на изменения в "../Сериалы". С другой стороны, все песни «The Doors» находятся внутри нее и их не должно быть в папке «Котики»).
  2. Уровень абстракции причины для изменения должен быть не выше уровня абстракции изменяемой сущности. Добавление в «Музыка» подпапки «Алла Пугачева» никак не может быть причиной изменения «Waiting for the Sun».
  3. Не нужно доводить до абсурда. Если у вас три песни, один видос и пять фоток котиков, то они прекрасно будут смотреться в одной куче — рассовывание их по папкам только все запутает. Как и сборник «The best of The Doors» не стоит разбивать на подпапки по годам, в каждой из которых будет по одной песне.

O — Принцип открытости/закрытости (Open-closed Principle или OCP)

Программные сущности (классы, модули, функции и т.п.) должны быть открыты для расширения, но закрыты для изменения («Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.» Bertrand Meyer)
Вернемся к тому шагу нашей истории, когда вы подключили скоростной безлимит. Допустим, что в первую очередь вы накачали всевозможных фильмом про сантехников кинокомпании Private и, так как они все были про сантехников, создали папку "/Видео/Про сантехников/".

Через некоторое время вы скачали еще фильмов этой студии, но уже про доставку пиццы. Мало того, ваша новая девушка написала вам СМС «Я скачала фильм „Афоня“ и сохранила его в папку /Видео/Про сантехников/, ок?». И вроде бы все верно — про сантехника, но есть нюанс.
И тут вам становится понятно, что изменение функциональности невозможно без модификации уже существующей структуры папок(классов), а это явное нарушение принципа OCP.

Как в этом случае надо было сделать? Очень просто — изначально спроектировать систему таким образом, чтобы новый функционал не требовал изменения старого кода. Т.е. не хардкодить папку "../Про сантехников/", надеясь, что в будущем там только про них и будет, а повысить уровень абстракции до "../Студия Private/" и спокойно скармливать ей и сантехников и разносчиков пиццы и прочая-прочая…

А для Афони создать новый класс, например "../Мосфильм/", расширив класс "/Видео/"

Выводы:

  1. Думайте наперед о том, что будете делать, если появятся «другие» фильмы про сантехников. Может стоит сразу по уму сделать, чтоб не переделывать потом?
  2. Этот принцип, в основном, про абстрактные классы ("../Студия Private/").

L — Принцип подстановки Барбары Лисков (Liskov Substitution Principle или LSP)

Объекты в программе должны быть заменяемыми на экземпляры их подтипов без изменения правильности выполнения программы.
Ну тут совсем просто.

Для объяснения давайте обратимся к милоте и няшности, а конкретно к папке "/Фото/Котики/".
Котиков мы любим и фотографий собрано у нас очень много.

Фото
  Котики
    Рыжие
    Полосатые
    Мокрые
    Черные
    Манулы


Бывает даже прямо запускаем слайдшоу по всей корневой папке и любуемся. И вот, в один прекрасный момент, когда вы уже практически достигли нирваны, на экран выводится:
Невозможно отобразить файл "/Фото/Котики/Книги/Кот в сапогах.fb2"
Как потом выяснилось, ваша подруга решила поднять градус милоты и отнаследовала «Котики» новой подпапкой «Книги», грубо нарушив LSP, так как подкласс «Книги» ну никак нельзя использовать вместо базового класса «Фото».

Выводы:

  1. Название пугающее, определение сложное, но сам принцип прост и интуитивно понятен.
  2. Если ваш метод ожидает на вход «Фото», то ему должно быть все равно, какого наследника от «Фото» вы ему подсунете: «Манулы», «Рыжие» или «Мокрые», но если ему на вход придет «Книги», то он, вполне ожидаемо, поперхнется чаем.

I — Принцип разделения интерфейса (Interface Segregation Principle или ISP)

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

Вам надоел стандартный проигрыватель музыки и вы решили скачать новый, супер модный и хайповый. Он даже умеет отображать обложку музыкального альбома и выводить субтитры исполняемой песни. Но вот беда, если в папке альбома отсутствуют файлы «cover.jpg» и «subtitles.txt», то проигрыватель падает с ошибкой. И вот вы, проклиная все на свете, начинаете создавать во всех подпапках с альбомами эти файлы заглушки.

Т.е., проводя не корректные аналогии, мы обязали класс «Музыка» и всех его наследников реализовывать интерфейс «AudioCoverSubtitles». При этом, полностью этот интерфейс реализует только альбом «Waiting for the Sun», альбом «The best of The Doors» реализует только часть «Audio+Cover», а все остальные только «Audio».

Это подводит нас к мысли, что имеет смысл разделить толстый интерфейс «AudioCoverSubtitles» на три небольших «Audio», «Cover» и «Subtitles» и применять их только там, где они действительно нужны.

Выводы:

  1. ISP — это, внезапно, про разделение интерфейсов.
  2. Если ваш интерфейс вынуждает вас создавать методы заглушки, то это плохой интерфейс и по нему стоит пройтись ножницами.

D — Принцип инверсии зависимостей (dependency inversion principle или DIP)


Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций.

Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Модуль «The Doors» не должен зависеть от того, какого рода аудио файлы в нем сложены, .mp3, .flac или .wav.

Модуль «The Doors», его подмодуль «Waiting for the Sun» (и все остальные), зависят от верхнеуровневой абстракции «Музыка», которая определяет их реализацию (то, что внутри у них музыка).

Допустим мы решили разделить хранение музыки по принципу сжатия — «с потерями» и «без потерь». Это детали, которые объединяет зависимость от абстракции «Музыка» — в них, в конечном итоге, должна таки быть музыка. При этом сама абстракция «Музыка» не зависит от этих деталей. Ей все равно, с потерями там музыка или без них — она как была музыкой, так ей и остается.

Выводы:

  1. DIP — это про то, частное должно зависеть от общего, а не наоборот.
  2. DIP — это «Больше абстракций богу абстракций!»
  3. DIP — это еще и про причины и следствия, про правильный ответ на вопрос «Ветви качаются от того, что дует ветер или ветер дует от того, что ветви качаются»

Спасибо за внимание и хороших выходных!

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


  1. godzie
    09.06.2018 13:35
    -1

    Disclaimer: Всем можно, ну а я чем хуже!?


    А чем лучше?
    Вангую следующее названия для «цикла статей о солид» — очень очень очень (PG-13) простое объяснение solid.


  1. nizkopal
    09.06.2018 13:42
    +1

    О, свежая тема! :)


    1. rjhdby Автор
      09.06.2018 13:52

      «об этой картине можно говорить бесконечно!» (с) Корона Российской Империи
      :)


  1. Gentlee
    09.06.2018 13:43
    +1

    А если фильм сняли две студии — Private и Мосфильм? Опять надо менять структуру? Или таки ввести «множественное наследование», чего вы не захотели делать в фильме про сантехника и пиццу? Вы даже сами не можете следовать принципам в примерах, о которых говорите. Еще и используете понятия ООП, которые по определению нарушают эти принципы.


    1. kiwokr
      09.06.2018 14:41

      Добавить в тот же уровень privare & Мосфильм. Множественное наследование? Ничего что наиболее массовые ООП языки его не поддерживают?


      1. godzie
        09.06.2018 15:02

        Наследование представляет собой отношение IS-A. Объясните пожалуйста. Как мне замапить в своей голове структуру видео\мосфильм\про_сантехников\Афоня на структуру классов. Что тут is-a чего?
        Лично я не могу говорить ничего об OCP ибо «простые примеры» вызывают неслабый такой диссонанс с привычными паттернами ооп.


        1. areht
          10.06.2018 12:36

          Афоня is a видео,
          Афоня is a из_мосфильма,
          Афоня is a про_сантехников


          1. godzie
            10.06.2018 16:18

            Афоня is a про_сантехников

            Как минимум тут что то лишнее, не находите?
            И да, такая структура не соответствует «видео\мосфильм\про_сантехников\Афоня».


            1. areht
              10.06.2018 19:33

              > Как минимум тут что то лишнее, не находите?

              в каком смысле?

              > И да, такая структура не соответствует «видео\мосфильм\про_сантехников\Афоня».

              class Афоня: видео, из_мосфильма, про_сантехников

              Если вы хотите поговорить про адекватность аналогии с файлами — тут я не помогу.


              1. godzie
                10.06.2018 19:41

                в каком смысле?

                Всмысле is a тут лишнее, «Афоня» про сантехников, про сантехников — это категория видео, «афоня» != категория видео
                class Афоня: видео, из_мосфильма, про_сантехников

                Ну так получается что видео, из_мосфильма, про_сантехников никак между собой не связанны. Хотя это не так, структура то подразумевает вложенность
                «видео\мосфильм\про_сантехников\...», а не «видео+мосфильм+про_сантехников\Афоня».
                Если вы хотите поговорить про адекватность аналогии с файлами

                Да я как раз об этом.


                1. areht
                  10.06.2018 20:35

                  > про сантехников — это категория видео

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

                  Да и как-бы не существенно сделаете вы «видео\мосфильм\про_сантехников\» или «видео\про_сантехников\мосфильм\»

                  Можете сделать вложенную структуру:
                  class мосфильм: видео;
                  class про_сантехников: мосфильм;
                  class Афоня: про_сантехников;

                  Но сам класс Афоня от этого не поменяется, как и его «is-a»


                  1. godzie
                    10.06.2018 21:12

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

                    Про возрастные ограничения не верный пример, от категории видео — не зависит (вернее не только от этого), в контексте мосфильма будет 4+ в контексте private 18+.
                    Можете сделать вложенную структуру:

                    Ну вот так как то и думает автор, я вас понял. Для меня же очевидно, что категория видео и производитель являются атрибутами «видео» и место тут имеет связь has-a а не is-a.


                    1. areht
                      10.06.2018 21:50

                      Это зависит от целей выделения «про сантехников». Вы говорите возрастные ограничения не зависят, а я, говорю, что не надо детям в листинге выдавать опцию private.


                      is-a тут не обосновано, но и ничему не противоречит — имеет право быть. Хотя пример так себе.


      1. Gentlee
        09.06.2018 15:26

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


      1. Sazonov
        09.06.2018 18:21

        Множественное наследование очень часто можно заменить на композицию


  1. cher11
    09.06.2018 14:35

    По-моему, аналогия с файлами на диске — не самая удачная. Особенно странно получается со множественным наследованием, так как в случае файловой системы у оригинального файла (не рассматриваем ярлыки и симлинки) путь к нему всегда один.


    1. rjhdby Автор
      09.06.2018 14:57

      Понятно, что если все написанное начать один к одному переводить в код, то волосы дыбом встанут в неожиданных местах. Суть поста (помимо того, что он пятничный) в «Попробуем разобраться в этих принципах на пальцах». Изначально вообще хотел назвать «SOLID для самых маленьких», но со студией Privat такое название не очень бьется.
      А так то толковых, но занудных и трудноусвояемых для неподготовленного человека статей про SOLID более чем в достатке.


    1. VolCh
      12.06.2018 12:31

      Ну как бы в большинстве случаев множественное наследование — нарушение SRP по определению.


  1. NYMEZIDE
    09.06.2018 15:01

    «Очень простое объяснение»

    почему-то вы еще больше запутали всякими котиками-сантехниками


  1. densss2
    09.06.2018 15:13
    +1



    RHCP
    Агутин

    Совести у Вас нет совсем!!! Фу таким быть! ))))))))
    З.Ы.: За статью — плюс)


  1. maslyaev
    09.06.2018 15:13
    +1

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

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


    1. rjhdby Автор
      09.06.2018 15:22

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


      1. maslyaev
        09.06.2018 17:45

        Если бы эти правила давали чёткие критерии, то это была бы строгая формализация способов строгой формализации… чего? Да вообще всего, потому что программирование мы применяем во всех мыслимых областях. Боюсь, о таком даже мечтать вредно.

        На мой взгляд, принципы SOLID полезны в первую очередь потому, что помогают удерживать ситуацию в состоянии «очень плохо», не давая ей скатиться в состояние «полный крах». Большего сложно требовать, поскольку сами базовые глубинные идеи ООП, скажем так, очень далеки от совершенства. Если навскидку, то нам до сих пор неизвестно, что же такое объект и что такое класс. То есть неизвестно, как завязать эти понятия на теорию множеств. Есть разные версии, но к каждой из них легко находятся фатальные возражения. Кроме того, есть даже ещё более глубокие проблемы. Например:

        1. Когда давным-давно нас учили ООП, основным аргументом за эту штуку было то, что поскольку реальный мир состоит из объектов, то если программы тоже состоят из объектов, они более естественным образом отражают реальный мир. Но в этом рассуждении сразу две ошибки. Во-первых, ни из каких объектов реальный мир не состоит. Наше восприятие для удобства осмысления раскладывает его на объекты, притом разных ситуациях по-разному. Во-вторых, программа — не зеркало, и ей совсем не обязательно что-то там отражать. Программа — это инструмент решения задачи. Молоток не обязан отражать процесс забивания гвоздя, он должен забивать гвоздь. Автомобиль не должен моделировать движение по дороге, он должен ехать.
        Можно заметить, что ООП оказалось чрезвычайно эффективным для создания вспомогательных абстрактных сущностей типа «строка», «файл», «массив», «бинарное дерево», «узел бинарного дерева», «функция» и т.п., а при создании прикладных сущностей типа «клиент», «товар», «пользователь» и др. вечно кривизна и геморрой. Объяснение простое: сущности нашего грешного «большого» мира слишком сложны, нечётки, многообразны, изменчивы и контекстно-зависимы, чтобы без проблем быть уложенными в прокрустово ложе любой жёсткой формализации. А абстрактным вспомогательным штучкам эта жёсткость только на пользу, потому что простота и предсказуемость — это как раз то, что нам в первую очередь от них нужно.

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

        Соответственно, для себя в дополнение к SOLID я стараюсь придерживаться двух дополнительных правил выживания в сложном мире ООП:
        1. Не пытаться моделировать программой реальный мир. Выкинуть вообще из головы эту глупую идею. Пусть все объекты в программе будут абстрактными вспомогательными полезняшками, а завязка на реальный мир пусть идёт через реализацию пользовательской метафоры. На верхнем уровне метафоро-ориентированный дизайн, а на нижнем ООП, но применённое по возможности исключительно к абстрактным инструментальным сущностям.
        2. Наворот третьего уровня иерархии — харам. Аллах покарает. Если вышел на то, что третий уровень логичен и полезен, то ищи уже допущенную ошибку в своих рассуждениях.

        Не знаю, какие буквы назначить этим двум принципам.


        1. rjhdby Автор
          09.06.2018 18:13

          Если бы эти правила давали чёткие критерии, то это была бы строгая формализация способов строгой формализации… чего?
          В предыдущем комментарии я несколько некорректно подобрал слово. Это, конечно же, не «проблема», а «особенность» этих принципов. В том смысле, что я невольно придал негативный оттенок тому, что не считаю плохим.
          Однако можно пофантазировать, что это были бы четкие критерии, позволяющие определить, соответствует ли код данному принципу. Например это позволило бы писать автоматические анализаторы на соответствие SOLID.
          любая иерархия с количеством уровней больше 2 неизбежно содержит внутри себя логическую ошибку. А когда начинают наворачиваться уровень на уровень, снова и снова, вся конструкция становится одним большим памятником человеческой глупости.
          Вы рассматриваете иерархию как дерево — эдакий развесистый граф на плоскости, тогда как более правильно (имхо) рассматривать ее как фрактал (плохая аналогия, но что уж придумалось), по которому можно провалиться вниз, либо подняться наверх. И, при работе с конкретным уровнем абстракции, вы работаете только с ним и с интерфейсами выше- и ниже-лежащего уровней, если они есть.


          1. maslyaev
            09.06.2018 20:00

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

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

            Рассмотрим дедукцию. Имеем некий сложный объект и пытаемся его декомпозировать на составные части. Для того, чтобы это сделать, нам нужно придумать принцип, который будем использовать при делёжке, а потом его применить. Например, человеческую популяцию можно поделить по половому признаку. Или по гражданству. Или по возрастным категориям. Имущество организации можно поделить на движимое и недвижимое. Или по способу учёта — основные средства или малоценка. Человеческий организм можно декомпозировать по функциям, т.е. на органы. Или по типам тканей. Какой бы мы принцип дедукции ни выбрали, его применение нам сразу даёт существенный профит. Появляется возможность говорить про взаимоотношения полов, про влияние госполитики на национальное самосознание, про межпоколенческие конфликты, про особенности хозяйственного использования разных предметов, и т.д. Имеем два уровня абстракции и кучу позитива. Природная жадность не даёт нам остановиться на достигнутом и подталкивает к тому, чтобы развить успех. То есть нарастить снизу ещё один уровень абстракции (напоминаю, мы занимаемся дедукцией). Применить тот же принцип делёжки мы не можем, потому что на предыдущем шаге мы его уже полностью исчерпали. Нужно брать другой. Если на предыдущем шаге мы популяцию делили по полам, то теперь нужно поделить каждое подмножество по, например, возрастам. А теперь, внимание, вопрос: есть ли какой-то реальный смысл в том, что половой уровень абстракции оказался выше возрастного, а не наоборот? Очевидно, нет. Мы с таким же успехом могли бы первую декомпозицию провести по возрастам, а вторую по полам. В итоге имеем: верхний уровень абстракции заслуженно занимает своё положение, а последовательность нижележащих — чистой воды волюнтаризм.

            С индукцией та же самая фигня. Тоже выбирается обобщающий принцип, и затем продуктивно применяется. Для следующего обобщения нужен уже другой принцип. И опять возникает вопрос: а с какого это бодуна у нас именно такая последовательность, а не наоборот?

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

            Для того, чтобы до глубины души прочувствовать абсурдность многослойных абстракций, ознакомьтесь с шедевром — общероссийским классификатором основных фондов (известен в народе как ОКОФ). Моя любимая фишка — элемент, к которому можно отнести Большой Адронный Коллайдер. Называется «Ускорители заряженных частиц кольцевые». Лежит в группе, в названии которой присутствует словосочетание «оборудование для балансировки шин» :)))


        1. VolCh
          12.06.2018 12:41

          > Объяснение простое: сущности нашего грешного «большого» мира слишком сложны, нечётки, многообразны, изменчивы и контекстно-зависимы, чтобы без проблем быть уложенными в прокрустово ложе любой жёсткой формализации.

          Вот потому один из принципов DDD — выделять чётко ограниченные контексты и моделировать сущности и вообще только в рамках них. Как раз в соответствии с SRP — сущность не должна моделировать книгу и в контексте физического объекта, и в контексте печатного издания, и в контексте товара одновременно. Вот в этой «папочке» Book у нас разновидность физического объекта, имеющая например массу и три измерения, а в другой Book — печатное издание, имеющее тираж, коды, копирайты и т. п., а в третьей товар, имеющий цену закупки, продажи, остатки на складе и пр. Причём в каких-то из папочек даже сущностью может не быть Book, если нам не интересен в данный момент поштучный учёт книг, что бы под словом «книга» мы не имели в данном контексте.


  1. LeqWiser
    09.06.2018 15:14

    А можно с примерами кода? Может, ссылочку на статью?!
    Не все до конца понял ;(


  1. ShaevMV
    09.06.2018 16:56

    Как потом выяснилось, ваша подруга решила поднять градус милоты и отнаследовала «Котики» новой подпапкой «Книги», грубо нарушив LSP, так как подкласс «Книги» ну никак нельзя использовать вместо базового класса «Фото».
    После это она больше тебе не подруга, она тебе враг)))


  1. dim2r
    09.06.2018 17:10

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


    1. VolCh
      12.06.2018 12:44
      +1

      А у геометрических фигур есть поведение? Какое поведение у квадрата? :)


  1. yuriy12
    09.06.2018 19:19
    +1

    Лично мне Ваш способ изложения не кажется таким уж простым. S.O.L.I.D нужен программистам. Они прекрасно знают что такое классы, интерфейсы, IoC и.т.д. Сантехники и котики IMO, это дополнительная энтропия.


  1. Dywar
    09.06.2018 19:46
    +1

    Очень коротко.

    SRP — не мешаем в одном месте работу разных по возможностям и уровням доступа пользователей. У которых разные желания и разные причины для изменения.

    OCP — то что часто меняется или добавляется выносим наружу, внутрь это дело прокидываем как стратегию/шаблон. Например, повар и рецепты которые он выполняет лучше хранить и развивать отдельно, навигатор и алгоритм расчета пути из точки А в точку Б.

    LSP — производный класс при привидении его к базому должен вести себя как базовый класс.

    ISP — не пытаемся засунуть все в один интерфейс, на то и позволена множественная реализация.

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


    1. VolCh
      12.06.2018 12:53

      > LSP — производный класс при привидении его к базому должен вести себя как базовый класс.

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


  1. fukkit
    09.06.2018 20:59
    +1

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


  1. slonopotamus
    09.06.2018 21:22

    Ваши аналогии подобны роялю на лыжах.


    Беда, в общем-то в самих принципах. Ну что вот это такое "A class should have only one reason to change". Любой класс меняют по одной единственной причине: он делает не то что от него хотят чтобы он делал. Всё, магическим образом все классы в мире стали solid?


    С другой стороны, любой класс будет меняться потому что:
    а) изменилось ТЗ на приложение и надо поменять логику класса
    б) в классе баг и он не соответствует ТЗ
    в) надо сделать класс более удобным для тестирования
    г) поменялось апи используемое классом


    Ну и где тут only one reason? В завистмости от того на каком уровне абстракции формулирую причины, их будет разное количество.


    1. areht
      10.06.2018 13:36

      > а) изменилось ТЗ на приложение и надо поменять логику класса

      Какого? Вы смаппите пункт ТЗ на класс и получите one reason to change. Без этого как вы выбираете какой класс менять, монеткой?

      > б) в классе баг и он не соответствует ТЗ
      > в) надо сделать класс более удобным для тестирования

      Это вы в предыдущий раз работу недоделали. «Я комичу какую-то фигню, поэтому SRP не работает» — это слабый аргумент.

      > г) поменялось апи используемое классом

      А вот и пункт ТЗ с reason to change нашелся.

      > он делает не то что от него хотят чтобы он делал.

      Это «хотят» — что-то коллективное-бессознательное? Сегодня от него хотят, что бы класс складывал 2 числа, а завтра захотят, что бы отрисовывал кнопку на экране? Если вы не школьную лабу делаете и класс не называется MyFirstClass — у вас конкретный класс меняется по более конкретной причине (причинам), чем «хотят что-то другое». Вот и выберите одну.

      > В завистмости от того на каком уровне абстракции формулирую причины, их будет разное количество.

      *поперхнувшись*
      Нет, от того, что вы одну причину сформулируете разными словами — причин больше не станет.


      1. slonopotamus
        10.06.2018 13:43

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


        1. areht
          10.06.2018 14:37

          Ну вот и не формулируйте таких глупостей.

          К чему эта софистика?


          1. slonopotamus
            10.06.2018 14:59

            К тому что принцип формулировку не дает и в этом его проблема.


            1. areht
              10.06.2018 16:20

              Если всем дает, а вам не даёт — это не его проблема, а ваша.

              За доведение до абсурда медалек не дают.


  1. Stalker_RED
    09.06.2018 21:54
    +2

    А потом появляется Pink Floyd — The Wall, который и музыка и одновременно с этим видео, и простая стройная система ломается?


    1. rjhdby Автор
      10.06.2018 18:00

      А это будет нарушением принципа инверсии зависимостей. Модуль высокого уровня «статья про SOLID» определяет детали реализации (аналогии с ФС), а не наоборот. Следовательно при разработке этой статьи в принципе не могло появиться «Pink Floyd — The Wall, который и музыка и одновременно с этим видео»


  1. MaxVetrov
    09.06.2018 23:55

    А где варианты с жесткими и символическими ссылками?
    А где варианты с 5 файлами HelloWorld.mp3 в разных папках?
    А где варианты с различными файловыми системами?


    1. rjhdby Автор
      10.06.2018 17:41

      Видимо в статье про устройство файловых систем, не? Тут их нет нет потому, что в контексте статьи они не нужны ;)


      1. MaxVetrov
        10.06.2018 18:00

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


  1. VladVR
    10.06.2018 01:10

    Я уже высказывался о том, что формулировка «отвественность должна быть единственной» — мутно определена. Также мутно, как и «водитель должен убедиться в безопасности маневра».

    А вот формулировка «слишком много ответственности» уже более понятна. И хотя бы есть критерии, по которым количество ответственности можно мерять, хотя бы косвенно. Количество строчек кода — если их много, значит ответственности много — надо декомпозировать. Слишком много импортов — тот же вывод. Слишком много тестов нужно написать, чтобы протестировать — опять тот же вывод.
    Пороги между «еще норм» и «слишком много», конечно же, индивидуальны. Кому то 800 строчек в компоненте dropdown это норм, две страницы импортов это норм, что такое тесты знают только понаслышке.
    А кому то 20 строчек в теле функции это уже сигнал.


  1. nexmean
    10.06.2018 09:46

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


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


    LSP — тип реализует Interface1 И Interface2 => тип реализует Interface1 ИЛИ Interface2


    ISP — SRP для интерфейсов.


    DIP — зависимость на интерфейсах вместо конкретных типов.


    Все эти вещи куда лучше работают с ФП идиомами, в частности с тайпклассами, трейтами и композицией вместо наследования.


    1. slonopotamus
      10.06.2018 14:01

      не важна реализация, важен интерфейс

      Является ли big-O сложность частью интерфейса?


      1. VolCh
        12.06.2018 13:00

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


  1. red-barbarian
    10.06.2018 17:42

    Спасибо за статью. Спасибо за новый взгляд на SOLID.
    Читая про сантехников, сразу подумалось «это же LSP». Оказалось не про то.
    LSP — это когда интерфейс, вроде, одинаков — фильмы. Вроде и общее есть — сантехники, а вот поведение другое. Ждут от фильмов рассказ о немецких сантехниках, а получают Афоню.