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
#14 Мой путь

Июнь 2014 - защита дипломного проекта, которая заняла буквально 5 минут. Так обычно и происходит в жизни, когда мы к чему-то очень долго и кропотливо готовимся, волнуемся, а это все оказывается мимолетным и заканчивается невероятно быстро. После чего возникают одни и те же мысли: и стоило ради этого так переживать! 30 июня было назначено вручение дипломов, но к этому времени нужно было успеть пройти обходной лист, без которого собственно диплом ты не получишь.

Обходной лист нужен для того, чтобы ты не остался что-то должен университету после ухода, вроде книжек в библиотеке. Но самое интересное было под последним пунктом - сняться с воинского учета. И все бы хорошо, но этот пункт имел подводный камень - кроме снятие с учета тебе выдавали повестку под роспись, что ты обязуешься явиться во второй половине лета в свой военкомат по месту прописки. И этот план, надо признать, действительно хорош, потому что продолжать учебу в магистратуре, а затем в аспирантуре я не хотел - мне с головой хватило учебы в университете. Других вариантов отсрочки от армии до 27 лет я не видел на тот момент, а именно этот возраст является в Беларуси максимальным для военнобязанных.

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

Служба в резерве предполагала сборы на 1-2 месяца 3 раза в течение 2-х лет, что в общей сложности получалось 4.5 месяца армии вместо 1 года, который полагался для всех ребят с высшим образованием. Естественно я выбрал резервную службу, потому что такой проект не вечен, где я бы смог получать отсрочки до 27 лет. А отслужить в армии не увольняясь из компании, да и еще во время обязательного двухлетнего распределения - звучало очень здорово. Осталось только ждать осени, ибо пока этот вопрос был решен.

Тем временем уже наступило 30 июня. Декан факультета торжественно вручил всем выпускникам дипломы, после чего мы своей группой оккупировали аудиторию в 4 корпусе БГУИР, чтобы тихонечко отпраздновать и выпить шампанское. Почему-то очень хорошо запомнился этот момент.

Далее последовал выпускной… и все, 5 лет прошло, и теперь спокойно можно выдохнуть. Начинается новый этап в жизни, который я также отметил переездом в новую двухкомнатную съемную квартиру, воспользовавшись моментом небольшого летнего спада спроса на них (чемпионат по хоккею закончился, да и студенты разъехались). Только в этот раз действительно достойную, с хорошим ремонтом, кухней и мебелью за 550$ в месяц, а не подобие сталинской коммуналки из 5 человек и рыжего кота. Правда, оплачивать одному всю стоимость было пока еще довольно накладно по бюджету, поэтому жил я с другом, вдвоем.

#my_little_story
🔥39👍157
Почему конспекты не работают

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

Как я уже писал в предыдущем посте, для закрепления информации нужно подключать как можно больше СЕНСОРНЫХ, МОТОРНЫХ и УМСТВЕННЫХ трудов.
Начнем по порядку.

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

Моторика - это произвольные движения нашего тела, для которых необходимо использовать сразу несколько систем: нервную, костную и мышечную. В нашем примере с конспектами - используются ТОЧНЫЕ движения ручкой. Эта уже даже мелкая моторика, которая подключает отделы нашего головного мозга еще эффективнее (потому так полезна для детей).

Это наш природный Output Stream. И как можно догадаться, он разительно менее эффективен, чем InputStream: все-таки получать информацию мы научились в гораздо больших объемах, нежели выводить ее из себя.

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

И именно этой третьей части мне всегда не хватало на лекциях, когда я писал конспекты. Потому что времени на осмысливание информации попросту не было. Я пытался законспектировать услышанное и все. Т.е. преобразовывал Input Stream полученный из слуховой системы (СЕНСОРИКА) - напрямую в записи в тетради (МОТОРИКА) с минимально обработкой, чтобы этого было достаточно для конспектирования.

Именно по этой причине мне никогда не нравились занятия в группах и посещения лекций в универе, я всегда любил заниматься самостоятельно. Для этого я могу использовать, например, видео уроки - их всегда можно пересмотреть с нужного тебе момента, если ты не успел ОСМЫСЛИТЬ услышанное.

Поэтому если правильно использовать это оружие конспекты - то можно получить неплохую пользу 🙂
👍33🤔12🔥91
Мое наблюдение о преподавании Java онлайн и оффлайн

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

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

💭 Я еще тогда подумал: “хм, прикольно, можно же записывать лекции, чтобы даже в таких ситуациях можно было ничего не пропустить и пересмотреть”. Но на этом мысль и закончилась и никуда дальше не продвинулась.

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

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

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

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

🎥 В принципе, тогда, 30 мая 2020 года, и зародился канал dmdev на YouTube и его самое первое видео.

Из очевидных плюсов записанных видео:
- возможность смотреть видео в удобнее тебе время. Не надо ехать куда-то сразу после работы, экономя время на логистику
- возможность останавливать, проматывать, пересматривать видео сколько угодно раз, чтобы ОСМЫСЛИТЬ информацию
- лектор запишет точно все, что хотел сказать, и ничего не забудет

Из минусов:
- нет возможности задать уточняющие вопросы лектору сразу по ходу видео
- нет обратной связи

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

Присоединиться на менторство DMdev, оставив заявку по ссылке:
👉Первая ступень менторства
👉Вторая ступень менторства

Старт: конец января 2024г.
P.S. Количество мест ограничено, группы по 10-12 человек.
👍24🔥9❤‍🔥5
Вопрос 👆 - Ответ 👇

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

Давайте разбираться…

Что самое важное в любом приложении? Конечно же это логика его работы.


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

Теперь самое интересное: математика целиком и полностью построена на логике. Это как следующий уровень после логики. Математика развивает мышление, учит анализировать и систематизировать знания, учит мыслить абстрактно и конечно же ЛОГИЧЕСКИ!

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

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


И в случае программирования - это в первую очередь логика.

Поэтому, отвечая на вопрос нужно ли знать программисту математику?
Ответ нет. Программирование не строится на ней.

Хорошо бы знать программисту математику?
Ответ да. Ибо транзитивно математика прокачает ключевые навыки для программиста.

#dmdev_qa
🔥33👍11❤‍🔥31
🎄 Как насчет ежедневных best practices вплоть до Нового Года?

С завтрашнего дня в рубрике "DMDEV ADVENT CALENDAR"

Новый день = новая возможность сделать твой код чуточку лучше!
Включай уведомления и зови друзей!
🔥16111👍11❤‍🔥3😍2🤯1
Не будь излишне терпим к null

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

Например, когда сравниваешь объекты
object.equals(CONSTANT)
- тебе необходимо учитывать, что object может быть null.
Yoda notation
CONSTANT.equals(object)
- обычно является той самой попыткой быть терпимым к null. Но зачастную такое сложнее читается, ибо выглядят не "естественно".


Поэтому:
- если ты НЕ ожидаешь, что object может быть null, object.equals(CONSTANT) - это отличный вариант написания.

- если ты ожидаешь, что object может быть null, то лучше использовать Objects.equals(object, CONSTANT) - это сделает код более чистым и понятным. Более того, он говорит, что object может принимать null значения.

- для параметров метода, которые не должны быть null, вообще лучше использовать fail-fast принцип Objects.requireNonNull (обычно генерируется автоматически с помощью тех же аннотаций Lombok)

#dmdev_best_practices
🔥85👍18❤‍🔥62👏2
Используй @Nullable в своем коде

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

Ее не обязательно использовать в коде, но если начал - то продолжай расставлять ее во всем своем проекте или модуле:
consistency превыше всего!

В противном случае, другие программисты будут считать, что если что-то не аннотировано @Nullable - то оно не может быть null.

Подытожим:
- используй @Nullable везде, если это возможно
- если не можешь поддерживать consistency во всем коде - то лучше избегай @Nullable (или везде, или нигде!)
- если используешь @Nullable, то смысла в @NotNull аннотации нет - ибо все и так по умолчанию будет восприниматься not null

#dmdev_best_practices
🔥60👍20❤‍🔥73😁1
Предпочитай кастомные аннотации, нежели использование @Named

Аннотации @Named и @Qualifier (Spring annotation) - очень мощный инструмент, позволяющий определять и использовать несколько бинов одного и того же типа. Вообще, спецификация JSR 330 Dependency Inject предоставляет два варианта: использование стандартных аннотаций вроде @Named, либо создавать свои кастомные. И именно второй вариант должен быть предпочтительным.

Основной недостаток стандартных аннотаций - это использование строкового значения в качестве идентификатора бина @Qualifier("primaryHttpClient")

Это неудобно и довольно легко допустить ошибку в написании, тем более что эти строки проверяются не во время компиляции, а только в момент уже создания бина (runtime). А если бин lazy - то это как бомба замедленного действия.

Конечно, существуют всякие статические анализаторы и подсказки от мощных IDEA, но они минимальны и не всегда помогают. Поэтому именно кастомная аннотация обеспечивает все необходимые проверки и помогает избегать типичных опечаток:


@Documented
@Retention(RUNTIME)
@Qualifier
@interface PrimaryHttpClient {}


#dmdev_best_practices
👍52🔥219❤‍🔥5💯1
Когда использовать Stream API

Императивный цикл for конечно же намного мощнее, чем обычная цепочка вызовов в Stream API.

Ты можешь:
- вернуться из цикла любой вложенности в любой момент времени
- изменять данные как угодно и где угодно (а не только affectively final)
- создавать несколько выходных результатов из цикла (а не только один)
- гибко обрабатывать и пробрасывать исключения
- использовать if, else и как угодно еще изменять ход выполнения программы

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

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

С другой стороны, благодаря таким ограничениям более простые алгоритмы читаются намного лучше и приятнее программистами.

Если программист знаком со Stream API - то по такому коду невероятно быстро схатываешь его суть, что он делает.

Поэтому правила просты:

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


P.S. А вот как представить многие задачи в виде последовательной цепочки шагов - еще надо научиться!

#dmdev_best_practices
❤‍🔥46👍33🔥242
Когда избегать Stream API

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

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

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


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

Также на своем опыте скажу, что API более высокого уровня я практически всегда определяю с помощью стримов, и только уходя вглубь - я могу прибегать к императивному стилю, если потребуется. Все потому что это очень сильно облегчает чтение программного кода, ибо я начинаю изучение всего “сверху вниз”.

#dmdev_best_practices
👍61🔥468❤‍🔥1
Создавай короткие lambda выражения
Когда ты используешь Stream API или Optional с его приятными методами map(), filter(), etc - избегай длинных и сложных lambda выражений в них.
Их не только становится сложно читать, но такой подход убивает всю суть стримов: их компактность и быстрое понимание общей логики кода. Благодаря компактности у программиста появляется возможность смотреть на код с высоты птичьего полета. А если нужны более подробные детали - то уже опускаешься глубже в каждый конкретный метод map(), filter(), etc.

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

Например, вряд ли кому-то понравится код, который выглядит примерно так:

.flatMap(
it -> {
try {
return readFile(it.getId()).stream();
} catch (IOException e) {
return Stream.empty();
}
})


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

.flatMap(fileDescriptor -> readFileAsByteStream(fileDescriptor.getId()))


Здесь мне каждая деталь помогает понять логику
- и название fileDescriptor
- и что я буду считывать файл на основании id из fileDescriptor
- и даже результат этого чтения: стрим байт

P.S. Заметил, что с каждым постом реакций все меньше.
Дай огня, если хочешь продолжение этого адвента.

#dmdev_best_practices
🔥366👍2710💯4❤‍🔥2🙏2
Ну вот как после такого не замотивироваться! Можете же порадовать старика 😅

Все, пошел писать следующий best practice на завтра 💬
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7822🔥14🤩7😁6❤‍🔥1
В которой раз убедился и практическим путем доказал даже на примере сегодняшних лайков - человек просто невероятно ленив

1️⃣ Если нет лайков под постом, то процент того, что человек поставит под ним свой - меньше, ибо нужно совершить больше действий (целых два клика! вместо одного)
2️⃣ Если не попросить реакции/комментариев, то их будет просто в разы меньше

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

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

Думаю, это малая цена за создаваемый полезный контент 🙂
142👍65🔥21💯14❤‍🔥7🤔3🤩3🙏2🥰1👏1😱1
👍19🎉42
Optional.isPresent(), .isEmpty(), and .get() методы обычно не следует использовать

Вышеперечисленные методы можно заменять на более выразительные конструкции Optional API.

1️⃣ Представим такую "классическую" логическую конструкцию if-else, где нужно вернуть значение:

if (optionalObject.isPresent()) {
return doWork(optionalObject.get());
} else {
return "other";
}

// Она может легко быть представлена двумя другими в зависимости от надобности в "ленивой" инициализации значения в else:
return optionalObject.map(this::doWork)
.orElse("other");

return optionalObject.map(this::doWork)
.orElseGet(this::doOtherLazy);


2️⃣ Порой нам надо сделать что-то со значением, если оно существует, и это действие void (в отличие от пункта 1). А если его нет - то ничего не делать. И такая конструкция:

if (optionalObject.isPresent()) {
doWork(optionalObject.get());
}

// Заменяется на:
optionalObject.ifPresent(this::doWork);


3️⃣ Похоже на пункт 2, но нам нужно сделать else действие, которое тоже void, если значение в Optional не существует.

if (optionalObject.isPresent()) {
doWork(optionalObject.get());
} else {
doOtherWork();
}

// Это можно заменить на конструкцию .ifPresentOrElse, которого очень не хватало в Java 8, и которое добавили только в следующей Java 9:
optionalObject.ifPresentOrElse(this::doWork, this::doOtherWork);


4️⃣ Очень похоже на пункт 2, только объем работы нужно выполнить больше, если значение в Optional существует, и порой приходится даже сохранять это значение в отдельную переменную. Это выглядит так:

Optional<Object> optionalObject = getOptionalObject();
if (optionalObject.isEmpty()) {
return;
}
Object object = optionalObject.get();
doWork(object);
// code ...
doWorkAgain(object);

// Вместо этого мы можешь просто воспользоваться .orElse(null), чтобы сразу получить значение из Optional и далее работать только с ним:
Object object = getOptionalObject().orElse(null);
if (object == null) {
return;
}
doWork(object);
// code ...
doWorkAgain(object);


#dmdev_best_practices
🔥198👍39❤‍🔥9🤯2😍21👏1
👍13🔥3
Названия тестов - очень важны!

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

Другими словами говоря, название теста служит для своего рода самодокументации.

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

Для интереса можно сравнить два примера одного и того же test case:

@Test
void processMessage_failed()

@Test
void processMessage_missingMessageId_throwsValidationException()


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


#dmdev_best_practices
🔥108👍385❤‍🔥5
👍8🔥2
Избегай boolean параметров

Если это возможно, то лучше избегать boolean параметров в public методах (открытый API). Все потому, что такие структуры очень плохо и читаются в коде, и пишутся, да и тестировать тоже неприятно. А создавать целые переменные только для того, чтобы описывать true/false флаг, программисты попросту не будут (да и не поможет это).

Например, глядя на этот код, можно только гадать, что значит true, и что будет, если в метод передать false:

Result result = processMessage(message, true);

Как можно этого избежать?

1️⃣ Джошуа Блох в своей книге Effective Java (Item 51) советует создавать enum с двумя значениями. Что и легко читается, легко документируется, и в случае чего - можно добавлять третье/четвертое значение и т.д.

Result result = processMessage(message, ProcessingType.DRY_RUN);


2️⃣ Заменить один метод на два других с более подходящими названиями.

Result result = processMessage(message);

Result result = processMessageDryRun(message);


3️⃣ Воспользоваться техникой уменьшения количества параметров в методах, т.е. просто создать объект, который будет содержать все параметры:

ProcessOptions options = ProcessOptions.builder().message(message).dryRun(true).build();

Result result = process(options);


4️⃣ Если по каким-то стечениям обстоятельств не помогли три предыдущие пункта, то нужно хотя бы оставить комментарий, что означает boolean параметр:

Result result = processMessage(message, /* dryRun= */ true);


#dmdev_best_practices
👍108🔥50❤‍🔥6