Как стать автором
Обновить

Комментарии 93

Это третья статья на тему «SOLID — что это за хрень».

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

ИМХО, что если принципы сформулированы так, что их нифига непонятны — это фиговые принципы. Для «простой» архитектуры невозможно использовать сложные принципы, которые нихрена не понять. Простая архитектура — «контроллер-логика-DAO», например, один раз прочитал — никогда не забудешь, или MVC или flux.

SOLID — как библия или Коран, каждый объясняет по-своему. То «не убий», то «сжечь ведьму»)

Для "простой" архитектуры не нужны принципы, это ещё php показал завоевав популярность всем кодом в одном файле.
Конечно не нужно цитировать solid с точностью до буквы, нужно их понять. А когда понял причины понимаешь когда следовать, а когда нарушать.

ИМХО, что если принципы сформулированы так, что их нифига непонятны — это фиговые принципы.


Есть мнение на этот счет:

Эта статья про то, что он может применяться намного шире, чем многие привыкли думать. А в целом является достаточно универсальной инженерной концепцией. Возьмите какую-нибудь гайку, там будет практически весь SOLID. Если вы не начинаете создание какого-то программного артефакта с понимания и выделения его зоны ответственности (SRP или "крепёжный элемент" на примере гайки), не вводите абстрактных интерфейсов (DIP или "метрическая резьба", "шестигранная" опять же для гайки) и т.п., то у вас есть проблемы. Вам так понятен MVC поскольку он — результат применения SRP.

Вот и про библию так же говорят)) Это философская «широкая» книга, где можно найти вопросы на все ответы)

Если вы к токарю придете и скажете ему сделай мне гайку М6 со стандартной резьбой. Он вам ее просто сделает. Не подохревая о СОЛИД, СРП, МВЦ и прочем.

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

Нет, не понял. Ни связи между "универсальной инженерной концепцией" и "философской широкой книгой", ни параллели с токарем)) Тогда можно любую книгу по теории струн, теории категорий или функциональному программированию сравнивать с библией. Относительно лаконичная теория широкого применения и многокнижие обо всём — это не одно и то же.


У токарей всё намного SOLIDней. Сама фраза "сделай мне гайку М6 со стандартной резьбой" — это DIP. В этой области уже давно провели SRP и ISP, аккуратно структурировав информацию по всяким DIN, SAE и т.п. Так что они-то, как раз, прекрасно об этом осведомлены и терминологией не нам их пугать)


Если вы придете и начнете ему затирать про принцип единой отвественности крепежного элемента с выделением в абстрацкцию его резьбы…

Вы знания вообще транспонировать не умеете? Нужно принципиально лезть в другую сферу со своей терминологией? Крепежный элемент — это уже выделение ответственности, а резьба — суть абстракция. Загляните в ГОСТ 27017 и посмотрите, как они расписали для себя SRP, DIP, ISP и какую терминологию используют. Если мы слово ответственность заменим на назначение, а абстракцию на стандарт, то мир рухнет?

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

Слишком много философии в весьма примитивные вещи, человек! Я об этом!))

Отличный ответ на моё удивление, что вы тащите специфичные термины в другой домен. Некорректная терминология часто встречается в брошюрках по демагогии)) Даже в нашей среде язык сильно различается и тот же ISP при работе с базами данных станет вертикальным партиционированием, а DIP станет представлениями. Но слесарь, конечно, должен пользоваться словарём из математики бинарных отношений. Вы с заказчиками и пользователями своих продуктов тоже на нашей тарабарщине общаетесь?))


Зато философии да, здесь много. Тот же слесарь говорит "гайка M7 под ключ на 12", где практически весь SOLID отражён, и чтобы это понять нужно, как минимум, четыре тома начал Аристотеля знать наизусть))


Так вот SOLID прост и важен как эта самая гайка. Возможно, он вам не сразу зашёл и оттого начались эти выдумки про его сложность. Но слушайте, скажем ФП ещё сложнее и термины ещё забористей. Может это просто не ваша сфера?)) У вас задача понять и разобраться, как я понимаю, в принципе не стоит?))

Если он прост, то почему уже третья подряд статья с попытками понять, что это за такая простая вещь, а внизу еще дерево споров, что автор не так все понял?
Я вам так скажу, я в детстве много читал, и грамотно писал, но никогда не знал правил русского языка. Я просто писал «ЖИЗНЬ» потому что у меня в голове не укладывалось что можно писать«ЖЫЗНЬ», хотя я и не подозревал про правило «жи-ши пиши через и».

И меня всегда забавляли попытки засунуть всю «ЖЫЗНЬ» в пять принципов, 10 постулатов, три завета, и устроить срач на тему, какой из принципов важнее. Иногда получается и круто. В данном случае не получилось, спасибо за попытку, выкиньте и забудьте.
Я просто писал «ЖИЗНЬ» потому что у меня в голове не укладывалось что можно писать«ЖЫЗНЬ», хотя я и не подозревал про правило «жи-ши пиши через и».

А какие-то люди так не могут, и им нужны правила.


Удивительным образом, с SOLID так же: кто-то интуитивно понимает, а кому-то правила нужны.

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

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

Но грамотно писать следуя из контекста они не начали, судя по всему?))

Почему же, они с правилами пишут грамотнее, чем без них.

Конечно правила нужны. Но принципы и постулаты, особенно чересчур общие — наверно нет. НУ только под пятничным пивком пообсуждать если)

не ради срача, но есть правила грамматики которые запоминаются легко и юзаются многими. Например, «жиши» или «тся/-ться»,

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

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

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


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


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


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


Вы мне чем-то моих детей напоминаете. Младшая ругается на арифметические операции, средний на тригонометрию. Аргументы почти те же))

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

Есть программисты-токари и программисты-инженеры. И не тешьте себя иллюзиями, 90% программистов — токари.

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

А какой контекст? И кто тогда сделал первую гайку M6? Врач? То есть в начале был ANSI/ISO и только потом стали использовать гайки?


Или всё-таки были слесари, которые в начале веков соединяли детали кто во что горазд. А потом к ним снизошёл SOLID, они выделили крепежную ответственность, выделили абстракции вроде метрических систем, сделали ISP в токарей и пошло поехало.


Скажем REST — это наши гайки. Получается, что программисты REST не делают, они REST описывают? Имхо, dididididi совершенно прав, вводят специализации. ИТ часто демонстрирует, что одна и та же сущность имеет множество аспектов, или категорий говорят языком теорката. И некоторого условного программиста мы можем видеть в самых разных ипостасях. Поэтому формулировка, что программист — это только X практически никогда не будет точной в искомой мере.

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

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

God Object очень легко построить на хотелках одних только бухгалтеров, поверьте. Я пробовал лет 20 назад.
Два разных предназначения приводят к нарушению этого правила.

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

Так god object и не обязательно нарушает single responsobility, вот инверсию зависимостей — целиком и полностью в любом исполнении.
Я не встречал лучшей интерпретации чем у Мартена, зато встречал кучу апологетов "единой ответственности" которых ставила в тупик следующая проблема: "если функция должна делать только одно действие то что насчет main которая делает все что делает программа"

Учитывая границы оперативной памяти:
ЕО не только одном действии, а для случаев, когда разработчику выгодно обозначить цепочку действий одним действием.
Пишем «удар» а на самом деле может быть:
расслабление
присед
отталкивание от опоры
запуск волны
возвратные действия

Таким путём, понемногу усложняя абстракции, превращая цепочки в ЕО, несколько непростых ЕО опять в цепочки… получится, что main делает одно действие.

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

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

А я и не говорил что только про классы, наоборот, я прямым текстом написал обратное. Однако в общем случае функцию модулем я бы не считал, даже в вашем высказывании не ясно с кем должна быть сильно связная функция?
Модули/классы они как раз и декларируют эту связность, это выделяется тем что они либо разделяют либо состояние либо детализированный интерфейс.
К примеру функции по работе с файлами fopen, fread, fclose можно выделить как модуль т.к. они разделяют состояние (файловый дескриптор) и разделяют детализированный интерфейс (файловый дескриптор — указатель на структуру, детали интерфейса закрыты для доступа снаружи, однако для этих 3х функций они доступны как бинарный АПИ, а не абстрактный интерфейс).

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

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

Даже если вы соберёте все хотелки от бухгалтеров в одном классе, но код будет на одном уровне абстракции этот код будет лучше, чем многое из того, что я видел.

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


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

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

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

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

Спасибо, немного увлёкся)

По моим представлениям, есть два общих принципа, упрощающих сложные программы. Здесь я их могу назвать «модульность» и «абстрактность». Все остальное (ООП, SOLID, ФП и т.д.) — способы достижения этих принципов.
Если эти принципы сбалансировано отражены в программе, то все будет хорошо независимо от деталей, типа соблюдения SOLID.
Модульность — это просто (на практике «просто» в кавычках). Нужно поделить систему на слабо связанные части с понятным поведением. Во многих случаях одной модульности достаточно.
Абстрактность — это посложнее. Здесь для себя, я представляю бесконечного размера программу, написанную для решения вообще всех возможных практически полезных задач за приемлемое время и занимающую наименьшее количество места (т.е. с близким к максимальному переиспользованием кода, где это не ограничено требованиями к скорости работы). В этой супер-программе можно выделить куски кода, которые для случайно выбранной задачи будут выполняться с большей вероятностью. Это будут самые «абстрактные» модули. Мозг каким-то образом способен отличать такие модули, когда он их видит, и способен генерировать их даже почти на пустом месте по каким-то намекам. Благодаря последнему свойству, в их работе часто (но не всегда) бывает легче разобраться (понимаем лучше то, что можем создать). И главное — у таких модулей по определению больше шансов быть вызванным без изменений в другой задаче и при другой постановке.
Дальше, нужны концепции, которые позволят сформировать границы модулей в коде программы: ООП, области видимости, женерики, чистые функции и др.
Первые две буквы SOLID как раз об абстрактности: в упомянутой супер-программе маловероятно исполнение (или даже наличие) модуля, который выполняет сразу две не связанные задачи или требует внутренней модификации для исполнения сходной задачи.
Последние 3 буквы — они на стыке поиска абстрактности и нюансов конкретного инструмента, которым мы нарезаем модули. L — абстрактнее модуль, не требующей еще одной своей версии для других входных данных, I и D — абстрактнее модуль, способный работать вне окружения (в тестах в то числе).
Правда, у всей истории есть еще одна совсем другая сторона: как трансформировать смутное понимание того, как все должно работать в работающий код. Здесь ООП и ФП как раз предлагают противоположные подходы.

Конечно существуют более общие и более абстрактные принципы как Low Coupling, High Cohesion а так же в целом абстракция. SOLID это более конкретные и более практичные советы на их базе. А уж двигаясь дальше в сторону практики и конкретики возникают паттерны.

При этом исследования принципов в разрезе модульности можно найти ( Low Coupling, High Cohesion), но исследований в направлении полезности абстракции не нахожу.
Хотя, это базовый инстинкт программиста (и не только): если все стало слишком сложно, нужно посмотреть на вещи абстрактно.

Почему нет? Полиморфизм это про абстракцию, DI, open/closed, инкапсуляция, паттерны это вообще все абстракции.

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

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


P.S. Наверное даже не "вместо чего-то", а просто "использовать, не погружаясь в детали"

Если мне нужно создать 20 объектов разных классов, предоставляющих друг другу некие сервисы, то я могу написать код который непосредственно все это развертывает, или могу написать абстрактный алгоритм для развертывания чего угодно (IoC-контейнер), зарегистрировать в нем эти 20 классов и использовать этот алгоритм для развертывания моих объектов. Второе решение более «абстрактное», чем первое. Почему? Как оценить «коэффициент абстрактности» объективно а не путем философских рассуждений? Иллюзия понимания и понимание — это разные вещи. ИМХО, этот фактор всегда недооценивается.

Ну вот, собственно, как 20 против что угодно. Ну или сколько изменений нужно сделать чтобы добавить 21-й класс.

Представьте, диалог:
— как сделать разработку более эффективной?
— используй больше абстрактных модулей;
— что это даст?
— их код легко переиспользовать и для изменения программы потребуется меньше изменений;
— ok, что имеется ввиду под «абстрактными» модулями?
— это такие модули, код которых легко переиспользовать и при использовании которых для изменения программы потребуется меньше изменений…

И второй момент: 21-й класс может иметь асинхронно работающий конструктор и допустим, раньше завершения инициализации объект нельзя передавать клиентам этого сервиса. Тогда при прямом создании объектов потребуется добавить пару строк, а при использовании IoC — переписать половину кода модуля-контейнера. Значит критерий «минимум изменений» слишком поверхностный, типа «жизнь — это способ существования белковых тел». Абстракция — нечто иное.

Абстракция нечто иное, но самые важные для программирования её свойства это именно минимизация количества изменений кода при изменении требований.


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


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

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

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

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

Бывает-бывает. Иначе бы вы просто не справлялись с задачами.


Скажем, когда я пишу программу на C#, мне (большую часть времени) не важно, какой IL получится в итоге, и никогда не важно, какими командами процессора это будет выполняться.


Теория, это абстрактные автоматы, машина Тьюринга

… и машина Тьюринга — это уже абстракция. Нет?

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

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

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

Концепция модуля — это уже абстракция. никогда не встречали простыни на десятки тысяч строк практически без всякого разделения на модули, подпрограммы и прочии абстракции?

В программировании не бывает важных и не важный деталей.

Бывает.


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

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


Нет деталей, которыми можно пренебречь, как в физике.

Полно. Программируя на языке высокого уровня я почти всегда могу абстрагироваться от того на процессоре какой архитектуры мой код будет исполняться, LE числа он использует или BE, например, RISC он или CISC. Не всегда, поскольку абстракции текут, но часто, очень часто. Или как выше указано — всё что от логгера мне надо — реализация им конкретного известного мне интерфейса (саботаж разработчика имплементации не учитываем). А там хоть админ пускай меняет имплементации.


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

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


И философия — это не теория.

Философия — это одна сплошная теория. И абстракция — её основной инструмент.

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

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

Микросервисы хорошие тоже соблюдают солид, правда пример для dip в голову не приходит

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

Вот, да, если сервис получает адрес своей зависимости (другого сервиса, БД, очереди и т. п.) не из енв-конфига-дискавери — то это злостное нарушение DI :)

Это хорошая практика SSoT, но к DIP не имеет отношения. Во-первых, сам дискавери-сервис — это микро-сервис. Получается, что он не DIP? Во-вторых, адрес может быть абстракцией. Скажем вы заходите на habr.com и нет разницы, как он реализован, пока сохраняются основные контракты — вы можете получать и поставлять контент. Фиксированный адрес может быть абстракцией вроде rest://production/security/login и в данном случае какой-нибудь ingress-controller вместе с DNS могут выполнять роль эдакого IoC-контейнера. DIP (а также ISP+SRP) — это сам факт использования микро-сервисов, через них мы привносим эти принципы в продукты.

Я бы не сказал, что не имеет отношение. Вы же сами пишите, что ingress-controller вместе с DNS могут выполнять роль эдакого IoC-контейнера

Поясню. Есть принципиальный уровень, где правит SOLID. Он говорит, что есть абстракция DNS протокола, а будет там CloudDNS, Bind9 или Unbound — не важно. Есть абстракция Ingress-controller, а Traefik, Haproxy или Nginx — пробуй любой. Тоже самое с service discovery. Это по определению сервисный уровень. Попытка поставить в зависимость от него принципиальный уровень (фразой "если сервис получает адрес своей зависимости не из енв-конфига-дискавери — то это злостное нарушение DI") — это серьёзный фейл в дизайне. Не может принципиальный дизайн зависеть от конкретных реализаций. Это всё равно что биохимию ставить в зависимость от полок соседнего супермаркета.


Попробую пояснить на примере pure-man инфраструктуры. Абстракциями в терминах DIP делаем адреса. То, что в коде выглядело бы как некоторый интерфейс SpecificContract стало /api/specific-contract. Собираем докер-контейнеры, которые реализуют конкретные контракты, например в docker-compose или ansible playbook с соответствующими labels для Traefik. Добавляю контейнер с Traefik + docker provider, готово. В любой момент я могу подменить контейнер с реализацией, но всё будет функционировать как ожидалось, поскольку DIP. И service discovery здесь даже не пахло.


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

Вопросы про SOLID с самого начала омрачались чрезвычайно серьезным к ним отношением, различными интерпретациями, нюансами перевода на русский и неутешительными реалиями практического применения. С ростом популярности ФП, попытки притянуть этим 5 буквам новые толкования, доводят хоть какие-то остатки смысла до абсурда.


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

Слушате, мне всегда было интересно, вот для чего вот это вот спрашивают на КАЖДОМ собеседовании? Вот что они такого хотят узнать обо мне как о разработчике, задавая этот вопрос? Даже не стесняюсь показаться тупым, но искренне недопонимаю, какую практическую ценность несёт этот принцип и какую информацию из меня хотят извлечь?
Ну то есть понятно, что в этом принципе есть некий здравый смысл о проектировании системы. Но почему это возводят в абсолют?
Интересно услышать мнение интервьюеров на этот счёт.
Уровень любознательности проверить как минимум =) Если знаете про SOLID — вы наверняка интересуетесь методиками создания хорошего кода в целом.
На практике это может означать, что перед вами человек, кому не лень зубрить к собеседованию разную хрень, поскольку он знает, что реальных достижений у него пока нет и возможно не будет.
От всего не застрахуешься.
кому не лень зубрить к собеседованию разную хрень

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

Сам по себе — нет, не засчитывается. Просто дальше будет какая-нибудь типовая задача на один из принципов. Если человек решит ее правильно — ну ок, неудобно, что он не знает, как это называется, но можно жить. А вот дальше будет вопрос, почему, если он "читал этот SOLID" и решает задачи в соответствии с ним, не видит применимости.

Допустим, тестовую задачу и ваши вопросы он «завалит» по пяти причинам: 1. Он привык плясать от целесообразности в архитектуре, а в тестовом задании ее нет 2. Половина в реальном соблюдении SOLID — это карго-культ, не более 3. Этот человек работал в другом мире, о существовании которого вы не подозреваете, где проблемы со сложностью были совсем другие (в вашем мире он сориентируется за неделю). 4. По складу ума он не способен на поверхностное понимание и принятие принципов, которые красиво объясняются логически, до того, как он реально столкнется с их необходимостью и сделает их частью своей целостной картины мира. 5. Он не любит когда его тестирует и дизориентирован.
Напротив, поверхностный человек может очень легко отбарабанить вам принципы одним левым полушарием, разъяснить все тонкости и обязательно учесть их в коде. Но потом выяснится, что это просто обезьянье подражание лучшим практикам без всякой реальной способности заставить эти принципы работать на пользу а не в усложнение.
Задавая академические вопросы неправильно можно дать преимущества второй категории людей по сравнению с первой. Я так полагаю, в этом заключается основное недовольство подобными вопросами.
Он привык плясать от целесообразности в архитектуре, а в тестовом задании ее нет

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


Половина в реальном соблюдении SOLID — это карго-культ, не более

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


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

А откуда мне знать, что он сориентируется в моем мире за неделю, если он не смог сориентироваться в его маленьком выделенном кусочке за время выполнения тестового задания?


до того, как он реально столкнется с их необходимостью и сделает их частью своей целостной картины мира.

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


Задавая академические вопросы неправильно можно дать преимущества второй категории людей по сравнению с первой.

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


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

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

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

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


Во второй раз всегда легче.

Это ваше допущение, с которым я не согласен.


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

Потому что тестовые задания, которые я даю — это срез моего мира. Их нельзя решить, не соориентировавшись.


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

А я не спрашиваю, классные они или нет. Я спрашиваю, что они означают и как применяются.


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

Лучше сделать и то, и другое. В смысле, и на код посмотреть, и про теорию поговорить.


Ну и можно дать практическую задачу просто для оценки общего уровня

… которую кандидат завалит "по пяти причинам".

Если у него нет своего мнения на этот счет, да, это более приемлемый вариант, чем «это карго-культ».
Вот здесь корень недовольства. Впечатление, что вы уверены, что владеете истиной в последней инстанции и ищете того, кто разделяет ваше мнение а не того кто будет эффективен в вашем коллективе. Эффективен будет умный человек, который видит и понимает больше а не тот, чье мнение волей судеб сложилось точно так же как наиболее обще-принятое в данный момент и в конкретной области разработки или ваше. Такая позиция не выгодна вам и не выгодна соискателю.
Потому что тестовые задания, которые я даю — это срез моего мира. Их нельзя решить, не соориентировавшись.
Выглядит как какая-то слишком идеальная ситуация.
… которую кандидат завалит «по пяти причинам».
Завалит, если искать там SOLID а не признаки ясного мышления.
ищете того, кто разделяет ваше мнение а не того кто будет эффективен в вашем коллективе

Я ищу того, кто будет эффективен в коллективе. Считать, что принятые в коллективе принципы это карго-культ — неэффективно.


Такая позиция не выгодна вам и не выгодна соискателю.

Я думаю, что я лучше, чем вы, знаю, что мне выгодно. А что выгодно соискателю… ну, это его дело.


Завалит, если искать там SOLID а не признаки ясного мышления.

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

У меня ощущение, что речь идет о довольно простых задачах, которые решаются с очень высоким вниманием к деталям. Поэтому программист должен как солдат соблюдать все регламенты и традиции и это самое важное.
В проектах где я работал проблема всегда была совсем другой: найти человека, который сумеет разобраться в ТЗ и продукте и создаст что-то вменяемое понятное и работающее. Не вынесет мозг тим-лидам и тестерам бесконечными итерациями и недопониманием, не утонет в сложностях алгоритмов. Какой у него получился код в итоге никого не интересовало. Код модуля всегда можно переписать, если функция прижилась и продукт выстрелил. Плохой код и архитектура опасны в ядре системы, но его пишут гуру, а их никто не тестирует на SOLID и так далее.
Лично я написал сотни тысяч строк нормально работающего кода и даже однажды написал свой фреймворк, реализующий задачи от ORM до графики на OpenGL, и посадил на него пару продуктов (до сих пор работают). Но про SOLID тест по прежнему не пройду наверно. Не понимаю и все. Для меня это список довольно поверхностных замечаний об ООП, ничем мне не помогающих в работе. Просто каждый из них по пунктам нарушал множество раз и каждый раз это нарушение все упрощало а не усложняло.
У меня ощущение, что речь идет о довольно простых задачах, которые решаются с очень высоким вниманием к деталям.

Если вы о моих задачах, то ваше ощущение неверно. Если вы о каких-то других задачах, то мне сложно судить.


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

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


Код модуля всегда можно переписать, если функция прижилась и продукт выстрелил.

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


Плохой код и архитектура опасны в ядре системы, но его пишут гуру

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

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

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

Да, теперь понятнее. Т.е. это некий код, который пишется не в один проход для одной задачи, а долго обсасывается, совершенствуется и много переиспользуется. К тому же разработчики меняются, и поэтому архитектура должна быть ясна как божий день. Поэтому если к нему нужно что-то добавить, то это надо сделать канонически правильно и элегантно, а ТЗ будет сформулировано совершенно недвусмысленно разработчиком, который это ядро отлично знает.
Если мне, к примеру, на собеседовании показали подобное ядро, а затем спросили про Solid и дали тестовый пример, то думаю я бы рассказал бы все красиво и написал тоже. Потому что здесь контекст понятен. Я правда, в таком не работал.
Здесь вопрос о SOLID и тест на способность формировать архитектуру под подобные требования выглядит логичным. Страшнее будет, если человек думает, что его берут какие-нибудь фитчи реализовывать в периферийных модулях, а тут ему — расскажи (покажи) как будешь использовать SOLID. А он думает «опять эти дурацкие вопросы и сейчас будут пытаться подловить меня на неправильном понимании SOLID, сами не понимая зачем он им нужен».
Теперь я знаю что отвечать, если спросят про SOLID.
Т.е. это некий код, который пишется не в один проход для одной задачи, а долго обсасывается, совершенствуется и много переиспользуется.

В идеале — да. Но не на практике.


ТЗ будет сформулировано совершенно недвусмысленно разработчиком, который это ядро отлично знает.

А вот это — точно нет.


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

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


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

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

А вот это — точно нет.
Выше вы сказали что ТЗ должно быть совершенно понятным. Как тогда выглядит ТЗ, тот кто его пишет и тот кто его выполняет — не могу представить.
Мне кажется, это противоречит высказываниям про карго-культ и «не понимаю я SOLID».
Все, это уже в прошлом. Вы будете смеяться, на я только что перечитал принципы SOLID применительно к описанной вами ситуации и осознал, что они совершенно очевидны в подобных условиях и ничего премудрого там нет. Я просто ни-разу так не работал. Когда я писал фреймворк, то писал его один и в свое личное время. Поэтому вылизывать архитектуру классов у меня не было времени, абстрации я просто держал в голове, не отражая из особо в коде, а выделить лишнюю функциональность из класса или добавить интерфейсы я мог в любой момент. Потому что я один и отлично знаю этот код и могу переворачивать архитектуру буквально за день как угодно.
В других задачах (прикладных) сложными были алгоритмы, но не было необходимости в сложных объектных архитектурах — объекты были крупными и так далее.
Ну и правильно, потому чтобы периферийные модуля в системе, где все устроено в соответствии с SOLID, работали корректно, они тоже должны играть по правилам. А если человек их не понимает, то могут быть проблемы.

Опять по тем проектам что делал смутно представляю себе где они там могут так уж что-то испортить не соблюдая SOLID. Обычно это требуемое ТЗ поведение (несколько связанных алгоритмов) в god-объекте, работа которого в целом понятна. Можно конечно понаделать абстракций с интерфейсами, но неуклюжие абстракции по мне разбирать потом хуже чем их отсутствие. А чтобы делать красивые абстракции — это не те люди и не те задачи.
Выше вы сказали что ТЗ должно быть совершенно понятным.

Нет, я этого не говорил. Вы точно ни с кем меня не путаете?


Я просто ни-разу так не работал.

Ну вот видите, вот, благодаря разговору про SOLID, и выяснилось, что у вас в опыте есть, а чего — нет.


Опять по тем проектам что делал смутно представляю себе где они там могут так уж что-то испортить не соблюдая SOLID.

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

Нет, я этого не говорил. Вы точно ни с кем меня не путаете?
Не путаю. Ну бог с ним. Я и так уже злоупотребляют вашей отзывчивостью.
Ну как, достаточно нарушить принцип подстановки, чтобы сломать другие компоненты, которые потребляют интерфейс, реализуемый этим.
Как это выглядит? Программисту поручают реализовать некий сервис с интерфейсом, программист понимает, что этот интерфейс с этим сервисом плохо совместим и вместо того чтобы поднять вопрос о расширении или изменении интерфейса он использует некий «хак» вольно интерпретируя смысл интерфейса, полагая что все будет хорошо, а то что может быть плохо никогда не произойдет. Примерно так? И от подобных ошибок программиста может застраховать понимание принципа подстановки? Кто его знает. Мы множество раз делали нечто подобное, когда фитчу ждут завтра, а в следующем релизе уже все равно все будет по-другому (хотя, в 99 случаях из 100 у интерфейса только одна реализация). При этом все воспринимают это как костыль или хак. Из моего опыта, хорошим решением это может посчитать какой-нибудь заросший шерстью бывший дельфист-одиночка, чтобы не беспокоить умных коллег. Но подобные товарищи требуют внимания и воспитания, если уж их взяли работать.
Программисту поручают реализовать некий сервис с интерфейсом, программист понимает, что этот интерфейс с этим сервисом плохо совместим и вместо того чтобы поднять вопрос о расширении или изменении интерфейса он использует некий «хак» вольно интерпретируя смысл интерфейса, полагая что все будет хорошо, а то что может быть плохо никогда не произойдет. Примерно так?

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

Так не бывает. Вы просто отписались, чтобы я отстал видимо.

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

Если вы чего-то не видели или не можете себе представить, то это еще ничего не значит.
В том и дело, что я крайне мало видел. Поэтому и спрашивал. Хотя наверно выглядело так, как будто мне хочется поспорить.
То что вы упомянули именно принцип подстановки было неожиданно. Это уже какой-то патологический случай. Из того что я видел не в ядре, SO нарушают сплошь и рядом, I и D тоже часто не используют по факту (потому что все в одном классе), хотя при том же не забывают формально написать интерфейс для каждого класса.
В том и дело, что я крайне мало видел. Поэтому и спрашивал.

Фраза "так не бывает" не похожа на вопрос.

Эта проблема есть. Не вы первый, кто упрекает меня в подобном способе ведения беседы.
Слушате, мне всегда было интересно, вот для чего вот это вот спрашивают на КАЖДОМ собеседовании?

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


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

Например мне почти всё равно, какие языки, платформы и инструменты знает разработчик. Если он не чувствует хотя бы SRP+DIP, то я с ним работать не буду (за исключением набора джунов на ковку). Правда, никогда не требую знания аббревиатур, просто нашупываю сам ген))

Как минимум, я проверяю что если на код-ревью напишу "не нарушай SRP/DIP/...", то мне не нужно будет объяснять что это такое вообще

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


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


Когда я спрашиваю про SOLID меня устроит ответ "не помню конкретных принципов, но вот каким должен быть проект". Я вообще уважаю людей, которые ищут причины. Почему в Java при переопределении метода можно сузить тип результата? Почему нельзя добавить исключений? Почему Spring рекомендует инъекцию в конструктор, а не в поле? Почему в проекте обычно пакеты верхнего уровня это models/controllers/services?

Почему в проекте обычно пакеты верхнего уровня это models/controllers/services?

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

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


А между прочим такое разделение противоречит даже классическим многослойным архитектурам, так как тут нет бизнес слоя (он обычно размазан по всем, а в пакете "models" обычно анемичные модели или вообще DTO) и подходит разве что если у вас вся бизнес-логика в базе данных.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории