Developer's mind
584 subscribers
16 photos
18 links
Программирую, обучаю, делюсь опытом
java/kotlin, spring boot, clean code, databases
обратная связь:
@Hcd5opza9bdcjid26fg
https://t.me/developers_mind
Download Telegram
Немного о паттернах

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

Представьте что вы идете к стоматологу и вам предлагают выбрать - идти к стоматологу, который недавно выпустился из университета и имеет стаж работы 2 года, или идти к стоматологу без образования, но со стажем работы в 5 лет. И давайте притворимся, что кто-то его пустил работать без образования и это норма. Так вот, к какому вы пойдете? Уверен что многие предпочтут пойти к первому - потому что какое бы образование ни было, оно все равно позволяет хоть на каком-то уровне понимать причины болезней и применять те инструменты, которые позволят достичь результата. Конечно у стоматолога с 5-летним стажем скорее всего лучше получается управляться с инструментами, но в нужных ли ситуациях он их применяет? Понимает ли он почему и зачем определенный инструмент нужно брать в той или иной ситуации.

И с паттернами в разработке та же история, - это очень мощные инструменты в руках разработчика, но непонимание зачем они нужны и какие проблемы решают ведут к тому что инструменты используются неверно. В последние пару месяцев я собеседовал пару десятков человек уровня Junior/middle, и только половина из них хорошо отвечает на вопрос "какую боль или проблему решает этот инструмент?".

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

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

Интересный эффект попыток применить принципы Clean Code заключаются в том, что инструменты (т.е. паттерны) вы можете переизобрести заново самостоятельно, но уже с очень глубоким пониманием концепций их работы и проблем, которые они могут решить.
https://habr.com/ru/post/521062/

Вчера написали пост на хабр про программу Ноль Смертей (Vision Zero), которой в России занимаются пока что активисты. Я в этой истории немного учавствую в качеству разработчика телеграм бота для репортов аварий. Если кому-то вдруг хочется поучавствовать в чем-то опенсорс, социальном и на котлине одновременно - то welcome https://github.com/asundukov/crash_map - там пока не добавлены issues, но идейки как доработать конечно уже есть.
year++ ;)
У меня новая фраза-фаворит с последнего рабочего митинга (да-да, некоторые уже работают):

"It's double broken"
сегодня ночью @quattique написала новый пост после долго отсутствия и это напомнило мне и про мой маленький уютный канальчик который покрывается пылью.

И так как я пока сходу не готов писать лонгриды (хотя есть о чем) напишу про короткую рекрутерскую тему - не так давно узнал что у нас крутая дорогая рефералка в американскую компанию. Так что если у вас вдруг есть знакомые Java-разработчики (или вы сами таковой) с 3+ годами опыта работы и если вы хотите подзарботать $2-2.5к просто за рекоммендацию - то welcome ко мне в личку. А сами вакансии очень приятные - полная удаленка, привязка зп к долларам, опционы, работа в международной команде (с коллегами из америки) так что английский тоже must have 🙃
Немного о том, как быть “так себе саппортом” - просто отвечать что “ну это потому что ты используешь мобильный браузер и там может и не будет работать” и это типо окей. Можно было еще человека послать использовать macOS и только Safari, например 🙈
Задачка, которую я придумал год назад и периодически спрашиваю на собеседованиях. Интересный факт что знания которые требуются чтоб на нее ответить - это джуновые знания Java и школьный курс математики. Но не каждый Senior способен дать правильный ответ. Попробуйте ее решить и только потом посмотреть в комментарии и пишите туда ваш ответ, можно приблизительный, обсудим!

Вот ее текст:
Сколько различных объектов класса String можно создать таких, чтобы их hashcode был одинаковым (например, чтоб он был равен 42)?
Недавно обнаружил у себя на проекте примерно такое.
Это даже побудило меня написать статью, которую я наверное скорое опубликую. В этом куске кода есть одна серьезная проблема и ворох проблем поменьше, очень похоже, кстати, на задачки на собесах.

Пишите в комменты что здесь не так.

И кстати, если у кого-то есть интересные примеры кусков кода, или даже с собеседований - тоже скидывайте, хочу серию разборов сделать что такое хорошо и что такое плохо.
а я никак не мог найти конструкторы и методы и поля класса.
Всегда казалось, что проблемы связанные с equals и hashCode настолько редковстречающиеся что чего толку их изучатью А вчера увидел парочку в коде, причем их редковстречаемость вполне перевешивает их потенциальную серьезность.
Что было: hibernate entity у которой был переопределен только equals, при том что equals сравнивал только id-поля. Были написаны тесты - например, в одном из них создавался HashSet из пары этих entites, entities были не сохранены в БД поэтому их id были равны нулю. В итоге при вставке в Set они вставлялись всегда в новый бакет, но фактически были равны межды собой (конечно тут еще отдельныее вопросики про использование mutable объектов в set). Просто добавление правильного hashCode сломало работу тестов т.к. в сет стал попадать только один элемент, пришлось доработать еще и equals.

Но, вообще, иногда волосы встают дыбом когда понимаю потенциальные проблемы которые может доставить отсутствие вот этих важных 3-9 строчек кода.

А вот недавно студенты скидывали отличное StackOverflow обсуждение как вообще работать с equals/hashCode и JPA: https://stackoverflow.com/questions/5031614/the-jpa-hashcode-equals-dilemma
Всегда хочется объект, который используется в singleton-сервисе или контроллере, сделать полем класса (или static-полем класса если сам объект пересоздается), чтоб избежать его создания на каждый реквест. Именно так я сделал, когда начинал работать с Java и вынес в поле класса SimpleDateFormat. Через пару недель иногда стали прилетать ерроры. Оказалось, что этот класс не ThreadSafe и при работе в многопоточном режиме при попытке одновременного парсинга из разных потоков оно может падать.
Выхода тут несколько:
- отказаться от common-объектов и создавать каждый раз новенький SimpleDateFormat
- обернуть SimpleDateFormat ThreadLocal-переменную, чтоб создавать каждый экземпляр не более одного раза в каждом потоке
- использовать альтернативы FastDateFormat из Commons Lang или DateTimeFormat из JodaTime библиотеки

я предпочитаю второй вариант.

А вообще все вышеописанное относится к любым stateful-объектам. Будте осторожны и всегда проверяйте, что используемые вами инструменты являются ThreadSafe при работе в многопоточной среде.
а вы что думаете?

если баг не воспроизводится - он пофикшен или это не точно?
Недавно меня спросили - чем плохо большое количество аргументов в методе или конструкторе?

1. Это сложно читать в коде и на ревью, если IDE без подстветки - можно запросто перепутать пару аргументов
2. Если эти аргументы передаются через 2-3 слоя - то возникают длинные портянки аргументов где опять же очень легко многое напутать, а добавление/изменение чего-либо приводит к каскадным фиксам.

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

способ 1: Рефакторинг
Если метод не удоволтворяет принципу #SRP, то при его рефакторинге количество параметров явно уменьшиться

способ 2: Группировка параметров в один объект
Если метод удовлетворяет принципу #SRP - значит и параметры этого метода можно как-то сгруппировать. Можно их сгруппировать в один объект, который, в свою очередь создавать с помощью #builder

способ 3: Вынос аргументов метода в поля класса
В некоторых ситуациях есть возможность все аргументы метода или их часть вынести в качестве полей класса, тогда сам класс можно создавать с помощью #builder, а потом вызывать метод с минимальным колиеством дополнительных аргументов
На LeetCode начался November Challenge. А здесь ребята собрались чтоб делиться своими успехами и обсуждать задачки: https://t.co/S2xMdrWPmT

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

@Transactional
public void method() {
try {
//logic
} catch (Exception e) {
// report with a new transaction
}
}

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

Как сделать правильно:

public void method() {
try {
// call transactional method
} catch (Exception e) {
// call report with a new transaction

}
}


И вообще любые транзакции надо делать максимально легкими и пытаться избегать любых внешних вызовов, иначе на консистентность данных начинают влиять внешние сервисы, а это очень сложно разгребать.
Chain-сеттеры
немного холиварная тема, но выскажу свои мысли по поводу таких сеттеров. Поводом послужило очередное использование библиотеки #Lombok и, в частности, аннотации @Accessors(chain = true)

public DomainObject setName(String name) {
this.name = name;
return this;
}


из плюсов:
- иногда это удобно
- это нормально для #immutable-объектов, хотя конвенциально для этого используют префикс with (например DomainObject withName(String name) )
- это нормально для #builder-ов

а вот минусов:
- во первых (в первую очередь если речь про Java), это не соответствуют конвеншенам о сеттерах
- во-вторых, это превращает метод из Consumer<T> в Function<T, K> что затрудняет его поведение там где требуется обычный #setter
- это затрудняет использование API класса - очень сложно понять, читая код, - мутабельный это класс или мне надо использовать новую переменную после вызова метода
- это так же может помешать в оптимизациях, которые JVM может делать для void-методов - методы становятся order-depended т.е. в цепочке сеттеров выключаются механизмы реордеринга

А вы что думаете? Используете такое?
FIRST принципы

Кроме уже всем известных прицнипов #SOLID есть и очень важный набор принципов #FIRST. Это принципы про написание тестов. В основном юнит тестов, но можно сюда добавить и интеграционные.

Итак, тесты должны быть:

Fast - быстрыми
Independent - независимыми
Repeatable - повторяемыми
Self-validated - самопроверяемыми (т.е. результатом теста может быть только “пройден” или “не пройден”)
Timely - вовремя написанными, в идеале до написания продакшн кода (TDD подход)

Если написанный юнит-тест не удовлетворяет одному из первых четырех принципов - это повод присмотреться правильный ли это юнит-тест.
First  принципы - Fast

Самый первый принцип из набора FIRST - это #Fast. Тесты должны быть быстрыми. В основном потому, что это кардинально меняет подход программиста к их запуску. Чем быстрее выполняются тесты, тем чаще девелопер видит что что-то пошло не так, и точнее знает где проблема, и быстрее ее решает. Сравните - увидеть ошибку через секунду после внесенной правки или спустя час после того, как они выполнятся где-нибудь в bamboo, и на почту прилетит отчет, что какие-то тесты не прошли. Хорошо, если во втором случае файл еще не закрыт, но в большинстве случаев уже переключился на другую задачу и потом нужно переключать мозг назад на старую задачу, смотреть в браузер, перепроверять, запускать - делать много дополнительных действий на несколько минут.

В общем, быстрые тесты ведут к сильному повышению эффективности разработки.
First принципы - Independent

#Independent (независимость) тестов - тоже очень важная штука. Имеется в виду независимость их как друг от друга, так и от окружения, в котором они запускаются. Если они зависимы друг от друга, то для запуска какого-то определенного теста придется запускать из скопом, что увеличивает время запуска (а это плохо). Если зависимы от окружения - значит где-то они не будут запускаться, либо где-то будут падать, чем начнут создавать проблемы либо вам, либо вашим коллегам. Так же нельзя забывать, что запуск тестов в многопоточной среде - это тоже запуск в определенном окружении. И если сами тестовые классы имеют stateful-природу (имеют состояние), то тесты получаются как зависимыми от этого состояния так и друг от друга. А потом еще и нарушают #Repeatable принцип, если начнут иногда падать при запуске с помощью многопоточного Runner’а, к которому многие проекты все равно приходят чтоб сделать запуск тестов быстрее.
First принципы - Repeatable

#Repeatable - повторяемость. Означает что ваш тест должен давать везде и всегда одинаковый результат - будь то ноутбук разработчика или суперкомпьютер в недрах компании. Смысл вполне должен быть очевиден - если тест иногда имеет false-negatives - т.е. иногда показывает что прошел, хотя должен был упасть - можно пропустить баги в продакшн. Когда падает изредка - приучает команду ретраить его просто до тех пор пока не пройдет, что не только раздражающе но и тратит время. Хотя само по себе “изредка падает” означает что что-то где-то пошло не так.

Есть у вас такие “изредка падающие” тесты?