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

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

НЛО прилетело и опубликовало эту надпись здесь
Тотально согласен. Уже устал джунам объяснять про Clean Code… наделают 100 интерфейсов, а потом путаются сами… Зачем? — ну так в книге написали…
Очень хорошо.
Стоит всегда его применять)
Нашел отличный источник про архитекруту, очень советую ознакомиться всем и опытным и начинающим: github.com/sairyss/domain-driven-hexagon

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

Я же мучаюсь с DI на проекте, разбитом на кучу маленьких микросервисов. Толку от него в таком масштабе нет вовсе, зато найти нужный класс и метод целая проблема, потому что все зависит от абстракций и прыгнуть не выйдет.
Планирую еще одну статью делать на следующей недел. Там буду поднимать тему DI. Сам же предпочитаю доработанную версию сервис локаторов. Если кратко, то там что-то вроде типизированных многоуровневых сервисов.
object ModuleNameServices : Services{
    val serviceX by services{ServiceX()} //cached
    val serviceY by services("key"){ServiceY()} //scoped
    val serviceZ get() = ServiceZ() //newInstance
    val serviceU = AnotherModuleService //ref
}

Сам же класс Services — под капотом хэшмеп для хранения скоуп и хэшированых сервисо + функции возвращающие делегаты (services), ну и для тестирования — ф-ция для моков (положить в хэш сервис) — обертки и тд.

А использую через конструктор
class SomeClass(services: Services = ModuleServices){
     val sX get()= services.serviceX  //   Вот тут важно get()=  - защити от рантайм модификации и статических ссылок
}

Плюс такого подхода — прямая связност кода (ктрл+клик и найти вожно всё), легко тестить, нереально запутаться, нету рантайм ошибок, нету кодгенерации.
Минус — не по клинархитектуре… нету интерфейсов и тд… хотя при желании можно и доработать
Я же мучаюсь с DI на проекте, разбитом на кучу маленьких микросервисов. Толку от него в таком масштабе нет вовсе, зато найти нужный класс и метод целая проблема, потому что все зависит от абстракций и прыгнуть не выйдет.

А что мешает использовать тот же DI, но отказаться от абстракций, а все бины делать и инжектить сразу как классы? Ведь в случае если у абстракции одна реализация, зачем делать преждевременную и излишнюю абстракцию, это типа известного принципа Хоара/Кнута о том, что «преждевременная оптимизация — корень всех зол», но для абстракций?

прыгнуть не выйдет

В Идее Ctrl+Alt+B — сразу переход к реализации.
А что мешает использовать тот же DI, но отказаться от абстракций,

А модульные тесты (unit tests) как под это писать? «Как по книжке» — реализовать интерфейсы имитаторами (mock) и заглушками (stub) — уже не получится.
Нет, способы тестирования и при таком подходе, конечно есть, потому как были времена когда интерфейсов (тех, которые ключевое слово interface) не было, но тестировать все равно надо было: типовой прием — подмена исходного файла с реализацией. Но все ли разработчики такие приемы (без которых нынче легко прожить) знают? И все ли языки и исполняющие системы это дозволяют? И как использовать фреймворки для тестирования под написание таких тестов, буде такое желание возникнет: они ведь «как по книжке» умеют работать, вроде как, все, а вот как по-другому — возможны варианты, неприятные.
Короче, как и при любом отклонении от общепринятой колеи — вопросы, вопросы…
Забыл упомянуть что проект на ноде, и с тестами без DI проблем ровно 0.
А что мешает использовать тот же DI, но отказаться от абстракций


Проект на ноде, главный разработчик пришел из c#. Для программистов c# и java зависеть от абстракций это нечто неоспоримо правильное, ведь гибкость, модульность, и не важно что ни один класс не будет переиспользоваться с какими-то другими зависимостями никогда, потому что в теории по книжкам это единственно верный путь. А для меня вот это вот все чуждо, я хочу обратно свой KISS, но времена такие, cишарперы массово мигрируют на ноду, это не шуточки, серьезно, очень много людей приходит из c# и насаждают свои паттерны.
зато найти нужный класс и метод целая проблема, потому что все зависит от абстракций и прыгнуть не выйдет
Вот эта проблема убивает одну из важных фич современных IDE, созданных вообще-то для удобства написания кода. :-)

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

Паттерн "Facade"!

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

Не стоит бояться длинных имен, ...


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

Взять, например, то же самое имя метода из этой же статьи: transformDateToString. А важно ли к имени метода добавлять деталь реализации (toString), корректно ли это и не нарушает ли это те самые правила, о которых все твердят? Разве не правильным будет именовать метод проще: transformDate?

Буду очень рад прочитать другое мнение на этот счёт.
Ну по порядку.
«не раскрывать деталь реализации»

transformDateToString — не раскрывает РЕАЛИЗАЦИЮ
transformDateToStringUsingSimpleFormatter — а вот такой — уже лишнее

transformDate

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

Опять же — имя должно быть лаконичным, но с учетом контекста. При нейминге я беру(стараюсь) одно из 2 правил:
1) это общепринятые и известные имена (i j для циклов и тд)
2) имя отражает логику / цель использования (с учетом контекста использования)

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

Не является ли деталью реализации ToString в имени transformDateToSring?

Нет не является, т.к. и Date и String вообще жёстко прибиты сигнатурой метода ;)

Действительно дурацкое имя.


Дату к строке приводят не потому что захотелось, а с какой-то целью: вывести в UI (и тогда это что-то типа .toHuman), залогировать (но логгер сам должен уметь это делать в конкретном формате), отправить через API (тогда нужен или timestamp, или isostring, т.е. то, что описано в контракте).

Правильно ли я понял, что если вместо наследования использовать композицию, SOLID превращается в SID?

Получается так. Но если совсем избегать наследования то не получится делать полиморфизм. Впрочем нет, получится при желании, но будет выглядеть как «без сахарочка». Плюс при композиции придется деинкапсулировать все protected в public, то есть открыть больше чем при наследовании. То есть страдают все киты ООП. Может тогда не ООП вам нужно?

OCP не только про наследование. Он про расширяемость в целом. Вот неплохой пример https://towardsdatascience.com/5-principles-to-write-solid-code-examples-in-python-9062272e6bdc


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

OL у тебя все равно остается. Его по прежнему можно успешно применять к модели данных в БД, к внешним интерфейсам системы и подобные штуки.
Огромная благодарность за статью! Почти такие же мысли вертятся «вот прям на языке», но облечь в форму внутреннего гайда или статьи всё никак не складывалось. Это вот всё нужно печатать и вешать на стену рядом с Keymap reference от IDEA:), после чего тыкать в соответствующий абзац на внутреннем code review.
Вы не задумывались о монетизации этого опыта — что-то типа курса переподготовки jun -> middle с практическими задачами на каждый паттерн? Сам бы с удовольствием прошёл :)
Этим и занимаюсь в рамках работы в компании. Про глобальную монетизацию пока что не думал, но благодарен за идею.
В предпоследнем примере упоминание слова manager зашкаливает. Что если убрать его, не станет ли лучше? Я вот частенько замечал в нашем проекте. Как только в названии класса присутствует слово manager, нужно готовиться к очередному god object с говнокодом
В примере несколько классов от огромного проекта, в рамках примера — выгляди может и вырвиглазно, но в рамках проекта — вполне приемлемо
НЛО прилетело и опубликовало эту надпись здесь
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории