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

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

Принцип подстановки Барбары Лисков можно сформулировать так:

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


Вот интересно, гдеж вы взяли такую формулировку этого принципа? В вики есть формулировки от Барбары, и от Роберта, и обе они, что характерно, используют термин «подтип» (subtype). Причем как в русской редакции, так и в английской. А вы это слово куда дели?
Спешу удовлетворить Ваш интерес. Из книги Роберта Мартина «Чистая архитектура», издание 2018 года, 77 страница. Слова «подтип» там нет. Изучать чистую архитектуру по википедии… Ну, такое… :) Уж лучше хабр :)

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

Во-первых, там слово "подтип" есть, как раз на этой самой 77й странице.


Во-вторых, взятый вами фрагмент относился к назначению принципа, а не к его формулировке (формулировки этого принципа на 77й странице нет).

Вы хотите, чтобы я выкатил неподготовленному читателю в ознакомительной статье о SOLID формулировку от Барбары Лисков? Вы жестокий человек :)
Видите ли, без этого слова данный принцип не имеет смысла.
НЛО прилетело и опубликовало эту надпись здесь
Добавлю чёткости. На предыдущем месте работы одна команда писала заказ для одного крупного ритейлера (привет, Николай). О S.O.L.I.D ничего не слышали (привет, Джонни). Пролетели по срокам более чем в 2 раза. На код без слёз не взглянешь.
У меня есть пример команды, которая свято следовала всем Солидам, Скрамам и прочим ТДД, и как вы думаете что? Правильно, они точно так же пролетели по срокам, потому что они не решали задачу заказчика, а писали красивый код и проводили ретроспективы на целый рабочий день
Тоже бывает. Но реже.

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

небольших и средних проектов

Да, но только, если проект «выстрелит» потом убить кучу времени разбор такого кода, чтобы запилить очередную фичу, которая нужна «еще вчера».
Нет, не проще. И уже на следующий день не понятнее. Попробуйте чистую архитектуру. Уверен, Вам понравится. Ничего хитрого там нет.
В интернете уйма статей с названиями типа «Просто о SOLID», «Понятнее о SOLID», «Еще понятнее о SOLID». А может что-то не так с самим SOLID, если нужно одно и то же разжевывать по сто раз?
Изначально статья называлась «Что не так с S.O.L.I.D», Вы попали пальцем в небо :)

Пожалуйста, объясните, как принцип единственной ответственности применяется на верхних уровнях иерархии системы?

Что Вы понимаете под верхними уровнями иерархии?

Ну, функция main() в С/С++ программе, которая делает много всего. Типа Adobe Photoshop.

main является компонентом самого низкого уровня.

Компонент Main — это конечная деталь, политика самого низкого уровня.
Он является точкой входа в систему. От него ничего не зависит, кроме работоспособности системы. Его задача — создать все Фабрики, Стратегии
и другие глобальные средства и затем передать управление высокоуровневым абстракциям в системе.


Роберт Мартин, «Чистый код».

Я думал, Вы про микросервисы, к примеру.

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


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

Если я правильно понял автора (и S в SOLID), то именно поэтому это не принцип "единственной ответственности", а принцип единственной зависимости от требований. То есть верхние уровни иерархии системы ответственны за одно требование, обычно какое-нибудь общее и не должны меняться от изменения компонентов/требований ниже.

Разбираясь с SRP, я обратил внимание на следующее: в оригинале (Robert Martin — Clean Architecture), финальное определение принципа звучит так:


A module should be responsible to one, and only one, actor.

Обратите внимание на фразу "responsible to", которую на русский язык все переводят как "ответственный за". Я не эксперт в английском, но, если не ошибаюсь, "ответственный за" переводится на английский как "responsible for". На сколько я понимаю, предлог "to" в оригинале говорит не об ответственности "за что-то", а об ответственности "перед кем-то/чем-то". Тогда, на мой взгляд, этот принцип на русском языке становиться намного понятней:


Модуль должен быть ответственным перед единственным актором (потребителем).


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

Да, так и есть.

То есть "одна функция должна быть вызвана только в одно месте".

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

Сделать N+1 модуль: по одному для каждого актора из внешнего мира и библотечный, который все они используют. Актором этого библиотечного будет программист, придкрживающицся, например, Dry. И причина для его изменения: во всех N модулях появился дублирующийся код.

Ох уж этот SOLID. В нем только LSP четко описан, но не Мартином, а самой Лисков, рекомендую читать оригинал.


Модуль должен иметь только одну причину для изменения.

Ню ню, смешно, что это подаётся как некая «мудрость» опытными разработчиками, да ещё и в форме догмы. Я понимаю когда джуны читают и радуются, им может казаться, что вот он, ответ на вопрос «как проектировать системы». Ну давайте посмотрим на SRP внимательнее.


«Причиной для изменений» может явиться любое событие в будущем. “Событие/причина» это множество «элементарных событий/причин» (вспоминаем тер. вер.) Очевидно, что если нам нужно иметь строго одну причину для изменений, то этой причиной может быть только «элементарное событие». Разложить всю реальность на элементарные события мы не умеем. Приехали. Можно выкручиваться, но тогда нужно реальность заменить моделью, в которой каких-то событий не будет. Например там может не быть переезда с Linux на Windows или задачи типа «нужно все ускорить в 1000 раз на том же железе». Но в реальности то все это и многое другое есть. В итоге SRP тем проще при унять, чем меньше об окружающей действительности знает применяющий, идеальное оружие воинствующей невежественности.


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


Могу поспорить, что мой один ППЦ помощнее всего SOLID будет.

достаточно подготовленный читатель

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

Когда ДПЧ завалит задачу можно будет решить, либо он не ДПЧ, либо модуль не ППЦ и скорректировать ситуацию :)
Вы видимо устали к пятому пункту и принцип инверсии зависимости описан также как и везде — сова из слов, которую в упор не понятно как натягивать на пень реальности.
Мы тут имеем дело профессиональными гадалками от программирования, не удивительно, что формулировки такие, что туда можно весь мир вписать при желании.
А у бухгалтерии ничего не поменялось, они считают зарплаты по тем же формулам, вот только в этой формуле теперь будут другие цифры, потому что calculateSalary() ходит в calculateOvertimeHours(), а там теперь по просьбе отдела кадров сверхурочные не 2, а 2,5. Упс…

То есть проблема в том, что код не покрыт тестами. При чём тут SRP?


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

То есть OCP создаёт проблему на ровном месте, ок.


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

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


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

Какое-то масло маслянное. Барбара точно это говорила?


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

Неизменяемый контракт? Мы точно про LSP говорим, а не про OCP?


Теперь сущность Person нельзя удалить! PersonService реализует интерфейс, в котором не объявлено ничего лишнего.

Замечательно, потом у нас появляются сущности, которые нельзя обновлять, потом которые нельзя читать, потом которые нельзя создавать. В результате мы приходим к тому, что все интерфейсы должны состоять из ровно одного метода. При этом пользователь всё равно может послать DELETE запрос к сущности, которая вроде как никакого удаления не поддерживает. А чтобы узнать поддерживает ли конкретный сервис удаление или нет, придётся использовать рефлексию. Только для того, чтобы кинуть исключение не в методе сервиса, а где-то в недрах веб-фреймворка.

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

НЛО прилетело и опубликовало эту надпись здесь

Пример interface crud extends cru не помешал бы статье

Стройте зависимости от абстракций. Не стройте их от реализаций.

А где тут инверсия? (закрываю глаза ладонями) А нету инверсии!
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории