DMdev talks
3.24K subscribers
156 photos
13 videos
89 links
Авторский канал Дениса Матвеенко, создателя DMdev - обучение Java программированию

То, что все ищут по Java:
https://taplink.cc/denis.dmdev

P.S. Когда не программирую - я бегаю:
https://t.iss.one/dmdev_pro_run
Download Telegram
Практика JUnit 5

📊 Немного статистики
:
- продолжительность видео 1:03:39
- затрачено времени на создание ~8 часов
- доступно на всех платформах

+ новая домашка для самостоятельного закрепления

👀 Смотреть на YouTube в свободном доступе
🔥83👍29🎉7❤‍🔥1🏆1
Scheduled Job

Как-то раз мне нужно было реализовать периодический процесс, который запускался бы раз в 5-10 минут и выполнял определенную работу:
- анализ/изменения данных в базе
- отправка сообщений в Kafka
- отправка пуш уведомлений затронутым пользователям
- многое другое

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

Первое, что приходит на ум - это конечно Scheduled Job в одном потоке. Но in-memory вариант не подходил, потому что было несколько instances приложения. А значит на каждом запустилась бы такая Job.

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

И тут на помощь пришли Named Locks в MySQL (в PostgreSQL они называются Advisory Locks).

С их помощью в своей Scheduled Job просто необходимо выполнить предварительно SQL запрос вида:
SELECT GET_LOCK(‘anyUniqueNameOfLock’, 5);
Если запрос вернул 1, значит Lock получен и можно выполнять работу, иначе - другой instance твоего сервиса уже ее выполняет и можно ничего не делать.
Главное в конце не забыть отпустить Lock:
SELECT RELEASE_LOCK(‘anyUniqueNameOfLock’);

P.S. В Advisory Lock можно и этого не делать - после окончания транзакции lock будет автоматически снят, что действительно облегчает работу и не нужно обрабатывать дополнительно исключения в случае проблем с RELEASE_LOCK.

P.P.S. На картинке представлен пример использования такого подхода на чистом Java Core
👍63🔥193
Февраль месяц хоть и маленький, но тянулся для меня очень долго. А все потому, что он был насыщен большим количеством событий, происходящих в моей жизни: подготовка к отцовству, покупка квартиры в Варшаве, Google layoffs, планирование отпуска и переезда, тренировки к соревнованиям по бегу на 5 км и т.д.

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

Я прочел 2 книги, штудировал официальную документацию и исследовал свои предыдущие проекты в поисках практического применения и полезности тех или иных фич изучаемого инструмента.

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

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

Так что следи за новостями!
👍100🔥41❤‍🔥7🎉32🏆1
Релиз нового курса - первое видео уже на YouTube!

PS. Будет доступен по подписке Lead и со временем на других платформах (Udemy, GetCourse).
🔥108👍14🤩3💋3🏆2❤‍🔥11🎉1
🗄 DMdev talks: перезагрузка

С ужасом заметил, что последний пост я написал тут больше месяца назад.

Как так вышло?

На самом деле все просто:
Был фокус и четкий план на телеграм = были посты
Нет фокуса = нет постов

Хочу вдохнуть новую жизнь сюда, ибо формат телеграма мне очень нравится!

Для этого нужна ваша помощь🙏

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

На основании этого составлю себе план к действию и буду радовать вас почаще, чем раз в месяц😁
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥54👍8🫡4❤‍🔥1👨‍💻1
Часто мне задают вопросы такого плана - когда не понятно как реализовать или упростить уже существующий функционал.

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

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

Можно словами в комментариях, можно прям скринами с кодом.

Если наберем несколько интересных вариантов, то я напишу свои мысли по этому поводу 😎
#dmdev_qa
👍25❤‍🔥4🔥3
Общими усилиями в комментариях мы пришли к гибкому решению!
Его я отобразил на картинке.

Единственное что я изменил - это тип процесса, сделав его Class<T> вместо enum для демонстрации альтернативного подхода.
В этом есть свои плюсы и минусы, как и в любом решении.

Плюсы:
- не нужно заводить перечисления (enum) и делать соответствия между ним и процессом
- не нужно передавать в ProcessService дополнительно этот enum, когда мы хотим получить обработчик процесса (или добавлять доп поле в каждый процесс)
- во время выполнения мы не получим ошибки, что получили из Map не тот обработчик (класс процесса сам же является и ключом)

Минусы:
- без enum не виден весь список возможных процессов
- не напишешь хороший тест, что на каждый процесс есть свой обработчик
- пришлось в ProcessService сделать явное приведение типов, потому что метод getClass в классе Object возвращает Class<?>

Что бы ты выбрал все-таки: enum vs Class<T>?
#dmdev_qa
🔥16👍91🤔1
Backwards compatibility

При создании любого открытого API, будь то HTTP, gRPC endpoint, public метод в Java - необходимо помнить про обратную совместимость.

А суть его в следующем:
код существующих клиентов открытого API не должен сломаться при обновлении этого API. Старые клиенты должны уметь работать с новой версией API.

К сожалении, не всегда очевидно - обратно совместимо ли какое-то изменении в API или нет.

Но есть несколько правил, которых можно придерживаться для этого:

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

2. Не делать поля обязательными в существующих сообщениях (в новых сообщениях тоже стараться делать поля optional)

3. Не удалять или переименовать уже существующие компоненты (переименование - это аналог “удалить + добавить поле”). Более того, придется продолжать заполнять старые поля, даже если они стали избыточными (redundant)

4. Не менять типы существующих полей

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

Вывод
Не так-то просто писать открытый API, особенно если им пользуется большое количество клиентов.
Да и становится понятно, почему в Java Core столько старых классов и методов 🙂
👍34🔥6❤‍🔥3💩1
Сode review н-нада?

Многие поддержали данное предложение, что ж, давайте попробуем реализовать его!

Кто хочет, чтобы я провел code review вашего кода - присылайте ссылку на github в комменты под этим постом.
Я рандомно выберу один проект и в следующем посте опубликаю краткое резюме по коду с ссылкой на код с моими комментариями

Условия участия:
- Проект опубликован на github
- Весь код в виде одного пул реквеста или коммита
- Чем больше кода - тем больше добавить описания о нем в README файле (чтобы я понимал о чем проект)
- Не удалять пул реквест или коммит после code review
- Согласие на то, что ваш разбор попадет в этот канал
- Ссылки жду до 6 мая включительно

P.S. Если зайдет рубрика, то можем ввести ее на постоянной основе, например, каждое первое число
#dmdev_code_review
👍47🔥16👏2❤‍🔥1
15.000 подписчиков на YouTube!
Это в 1,5 раза больше людей, чем в городе, котором я вырос.

И всех этих людей связывает один общий интерес - Java.

3 года прошло, как я создал канал DMdev.
И он совсем не похож на среднестатистический канал ютуба.

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

За это время:
- уже более 1.000.000 просмотров
- 666 видео
- 12 курсов (пройдя которые - действительно можно стать разработчиком)

Благодарю каждого за доверие и активное участие!
Без вас - ничего этого не было бы🙌🏻

P.S. 🔔 Напоминаю, что сегодня последний день, как я жду ваши ссылки на гитхаб для code review (подробнее в посте выше)
🔥82👍16🎉8❤‍🔥2😈1
#dmdev_code_review
Как и обещал, выбрал одного из участников, чтобы провести code review.

Хотелось бы отметить общие частовстречаемые ошибки, которые в то же самое время очень важны:

1. Не следует изменять статические поля в нестатическом контексте. Чаще всего это приводит к ошибкам в многопоточности или попросту неверно работает код.

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

3. Не стоит писать код на исключениях - клиентский код потом становится громоздким с кучей try/catch блоков. Пробрасывать исключения следует тогда, когда действительно произошла ИСКЛЮЧИТЕЛЬНАЯ ситуация, которую никак не избежать. Например, гораздо приятнее возвращать true/false в методе delete вместо пробрасывания исключения, если такой сущности не оказалось по переданному id.

4. Связанно с предыдущим пунктом: не нужно проглатывать исключение, пробрасывая свое кастомное - иначе теряется stacktrace рутовой ошибки и будет очень сложно понять что произошло. Если нужно обернуть одно исключение в другое - просто передавайте его как параметр в конструктор: new DatabaseException(sqlException)

5. Разумно подходить к использованию Optional & Streams. Какой-то код лучше писать в функциональном стиле, какой-то в императивном, какой-то в комбинации двух предыдущих. Но в любом случае стоит граммотно использовать все подходы. Возьмем простой случай: возвращать Optional из метода, который может вернуть null. И, наоборот, если метод всегда возвращает объект - то не стоит оборачивать его в Optional, ибо это доставляет неудобства клиенту.

6. Давать хорошие имена переменным, методам, классам - всему в коде. Иначе очень сильно усложняется чтение кода, на которое программист тратит большую часть своего рабочего времени. Особенно избегать таких имен, которые подразумевают только ЧТЕНИЕ, а по факту там происходит изменения состояния объектов - т.е. ЗАПИСЬ. Например, findProduct(productId) - а внутри изменение состояния объекта Product.

P.S. Весь список комментариев по code review можно найти в пул реквесте по ссылке

P.P.S. Если есть вопросы по коду/моим замечаниям, что я оставил - то можете задавать их прям здесь под постом в комментариях
🔥49👍11❤‍🔥4
This media is not supported in your browser
VIEW IN TELEGRAM
Ребят, в конце июня стартую последнюю 2-ую ступень менторства в этом году

Осталось 4 места 0 мест.
Так что, кто желает окунуться в интенсивное обучение Java со мной - welcome.

Записаться и почитать подробнее можно по ссылке:
https://dmdev.tilda.ws/second-level
😁20❤‍🔥3🔥3
Попросил ответить на три вопроса ребят, которые на этой неделе окончили 2 ступень:

1. Если описать тремя словами, как для тебя прошло менторство?

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

3. И какой совет дашь для тех, кто думает/сомневается - идти или не идти

Ответы на фото👆
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥18👍8❤‍🔥5
#dmdev_qa - под этим хештегом отвечаю на ваши вопросы.
Вопрос на фото, ответ ниже👇

Этот подход зародился очень давно, можно сказать, исторически так сложилось. Даже в книгах писалось, что так нужно делать. Раньше это считалось best practices.

Потому что:
1️⃣ Ты легко можешь создать Dynamic Proxy, не прибегая к сторонним библиотекам и делая прокси через наследование, ибо у тебя есть интерфейс со всеми методами. Но сейчас по умолчанию даже Spring делает cglib прокси через наследование, ибо этот подход оказался более жизнеспособным.

2️⃣ Ты можешь описывать документацию над методами в интерфейсах или выносить туда константы (например, длинные SQL запросы для Repository), чтобы не делать этого в классах. Но сейчас тоже не актуально, ибо интерфейсы созданы именно для функционала, а не как хранилище констант, и второе - современные среды разработки и так умеют сворачивать документацию над методами, чтобы не смущать программистов.

3️⃣ Удобно было раньше создавать всякие заглушки в тестах или тестовых окружениях - просто создавая еще одну реализацию от интерфейса и ее подставлять вместо реального объекта. Но Mockito и без этого справляется и опять же через наследование.
👍264🔥4❤‍🔥2
🗓️ 1-ое число месяца, а это значит «время code review»

Кто хочет получить code review своего кода ➡️ присылайте ссылку на github в комменты под этим постом.

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

Условия участия:
Проект опубликован на github

Весь код в виде одного пул реквеста или коммита

Чем больше кода - тем больше добавить описания о нем в README файле (чтобы я понимал о чем проект)

Не удалять пул реквест или коммит после code review

Согласие на то, что ваш разбор попадет в этот канал

Ссылки жду до 5 июня включительно

#dmdev_code_review
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17🔥95
Теория бесполезна без практики

Сейчас сел перечитывать книгу “От 800 метров до марафона” Джека Дэниелса, которую прочел еще в прошлом году, когда только начал заниматься бегом. Только в этот раз я осознал, на сколько возросло мое понимание написанного в этой книге. Каждое предложение просто отпечатывалось в моем мозгу (наверное, даже на всю жизнь). Тогда как в прошлый раз - все просто выветривалось и очень быстро.

Если раньше я мог пролистывать часть страниц, думая, что это “не важно”, или давая себе совсем другие смыслы написанному, нежели закладывал автор (пытаясь понять и запомнить по-своему) - то сейчас как будто мир заиграл новыми красками. В голове всплывал лишь один вопрос - как я не понимал этого 10 месяцев назад?

А ответ довольно прост - я все эти 10 месяцев просто практиковался в беге, постоянно и методично. Экспериментируя, анализируя все, что делаю “на практике”.

Как здорово, что один и тот же принцип “теория-практика” можно применять в любых сферах: как в программировании, так и в таких видах спорта как бег.

Практикуй то, что изучаешь!
👍588🔥3🤔2❤‍🔥1👏1
Может все-таки велосипед?

Каждый раз, когда приступаю к написанию какого-то функционала, руки так и рвутся создать что-то с “нуля”. Чтобы было в этот раз уж точно гибко, расширяемо, без багов, а главное - свое!

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

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

Например, если мне нужно создать endpoint, который возвращает список чего-либо, я просто посмотрю как его реализовывают такие компании как Google, Amazon и т.д. Крупные компании имеют свойство хорошо документировать свои решения и даже выкладывать его в public доступ.

Поэтому, я открываю API документацию (в нашем случае list endpoint) и начинаю читать ее. И практически всегда я точно нахожу для себя что-то полезное, о чем “не подумал“, и что могу переиспользовать для своего решения 🙂
👍51🔥6❤‍🔥4
Ребят, кто пропустил информацию, то сейчас идет набор группы на менторство 1 ступени.

Старт: 4 сентября
Продолжительность: ~3,5 месяца
Свободно: 5 мест 1 место

Вся подробная информация о менторстве DMdev и запись тут:
https://dmdev.tilda.ws/first-level

PS. Вопросы можно также задавать здесь в комментариях
PPS. Пример финального проекта по окончании менторства есть на YouTube (проект можно выбрать абсолютно любой)
🔥14👍54
Полнотекстовый поиск

Пожалуй, один из немногих инструментов, который остался, надёжно закрепился и активно используется у меня при решении многих проблем при разработке ПО - это полнотекстовый поиск!

Хочешь найти применение класса, метода, куска кода в каком-то репозитории, да даже любой интересующий тебя ключ в yaml файле? Используй полнотекстовый поиск (особенно когда нет возможности открыть проект в среде разработки или это заняло бы неоправданно много времени).

Из совсем недавних примеров…

1. На менторстве при старте интеграционных тестов происходило обращение к Postgres по урлу 192.168.0.150:5432, хотя должен был определяться порт динамически из библиотеки testcontainers. Полнотекстовый поиск по ip 192.168.0.150 - и оказывается этот урл указан в забытом файле hibernate.cfg.xml.

2. Точно помнишь, что в каком-то видео на каком-то курсе dmdev слышал/видел информацию об аннотации @DynamicPropertySource. Открываешь индекс dmdev, ctrl + F - и находишь то, что хотел.

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

PS. И это я еще забыл упомянуть, как он меня выручает при поиске чего-либо в многотерабайтном монорепозитории гугла, который в принципе невозможно открыть и развернуть локально
👍203🔥3❤‍🔥1