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
Ну вот как после такого не замотивироваться! Можете же порадовать старика 😅

Все, пошел писать следующий 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
Открываю набор на менторство DMdev

Кто хочет в Новом Году наконец-то покорить Java и заложить крепкий фундамент на будущее в IT?

Стать конкурентоспособным разработчиком на этапе собеседований и/или внутри своей компании?

В связи с этим, приглашаю на менторство DMdev первой и второй ступени начинающих и практикующих джавистов!

Что внутри?

- недельные спринты с уроками и домашним заданием
- два раза в неделю живые созвоны с опытным ментором-разработчиком (на второй ступени со мной)
- еженедельное code review
- безлимитное общение в чате с ментором


Результат первой ступени менторства - готовый веб-проект в CV, написанный чисто на Java Core.
Результат второй ступени - веб-проект в CV, написанный на Java с использованием фреймворков (Hibernate, Spring)

Проекты не шаблонные, можно выбрать по своему желанию, чего душа желает

Старт: 22 января
Продолжительность: 3,5 месяца
Количество участников: до 12 человек

Оставить заявку и узнать подробности можно по ссылкам ниже:
-
первая ступень менторства
-
вторая ступень менторства

P.S. Это может стать одним из лучших подарков для себя 🎁 🎄
Please open Telegram to view this post
VIEW IN TELEGRAM
👍33🔥16❤‍🔥3
Используй Builders

Builders - это короткоживущие mutable объекты, которые имеют одну единственную задачу: собрать все необходимые данные, чтобы создать другой объект, который в свою очередь обычно immutable и долгоживующий.


User user = User.builder()
.firstname("Ivan")
.lastname("Ivanov")
.age(35)
.build();

На примере представлено классическое использование этого паттерна:
- объект builder короткоживующий (можно сразу его забыть, потому что даже не пришлось создавать переменную для его хранения)
- используется лаконичная цепочка вызовов методов (fluent API)
- установка всех параметров самодокументируется и сложно спутать один с другим

Когда следует использовать паттерн Buil
der?
В большинстве случаев для замены статических фабричных методов или конструкторов.


User user = User.of("Ivan", "Ivanov", 35);

User user = new User("Ivan", "Ivanov", 35);

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

Более подробную информацию можно найти также в книге Effective Java (Item 2)

#dmdev_best_practices
👍82🔥35❤‍🔥6
Kонструктивная и деструктивная критика в Code Review

Если простыми словами, то критика - это указание на недостатки (реже достоинства) чего-то, что сделал другой человек. Она бывает конструктивной и деструктивной.

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

Деструктивная критика, наоборот, бьет по самооценке. Она обвиняет человека и нападает на него. Деструктивная критика необъективна, не аргументирована и зачастую звучит унизительно.

Критику мы наблюдаем во всех сферах жизнедеятельности человека, особенно деструктивную в комментариях под моими видео 😁
Но где чаще всего мы ее встречаем в программировании? Правильно - это Code Review, где мы оцениваем код другого человека и оставляем свои комменатрии. И конечно же, нужно использовать только конструктивную критику во время Code Review.

Другими словами говоря, важно вести себя вежливо и уважительно, а также быть предельно ясным и полезным для разработчика, код которого ты просматриваешь. Один из способов сделать это - всегда комментируете ТОЛЬКО код и никогда не комментировать разработчика. Например:

Плохо:
Зачем ты использовал здесь многопоточность, когда очевидно, что от параллелизма нет никакой пользы?


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


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

#dmdev_best_practices
👍104🔥37❤‍🔥6👏3💯1
Параллелизм - это сложно

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

Например, если бы мне нужно было решить проблему многопоточности, то я бы следовал следующим шагам:
1. Сначала глянул на существуюющий фреймворк, может ли он мне помочь с проблемой. Это самый безопасный вариант, который не только помогает избегать багов, но еще и предлагает кучу метрик из коробки.
2. Если нужно решить low-level проблему, то конечно же я открыл бы пакет java.util.concurrent
3. Если все еще недостаточно, то я прибегнул бы к synchronized блокам или volatile (для неблокирующей синхронизации)
4. Только здесь я бы начал думать о своем собственном решении, если ничего другого не помогло, предварительно глянув решения в других проектах (возможно, кто-то уже реализовал)

В любом случае, я уже советовал книгу "Java Concurrency in Practice" в своих курсах (Java Level 2), если кто-то хочет действительно разобраться в многопоточности. Ну и конечно же нужно очень хорошо понимать Java Memory Model.

#dmdev_best_practices
👍75🔥255❤‍🔥3
👍4
Избегай какой-либо логики в конструкторах

Самый простой и общепринятый вид конструкторов - это присваивание параметров полям объекта, которые имеют те же названия и типы.
Если ваш конструктор выглядит не так, то подумайте, возможно, он все-таки должен быть таким:

User(Long id, String username, LocalDate birthDate) {
this.id = id;
this.username = username;
this.birthDate = birthDate;
}

Чем больше логики конструктор включает, тем сложнее тестировать такие классы. И особенно писать тесты на те классы, которые транзитивно зависят от них. Частично эту проблему решает Mockito, который позволяет создавать mock объект не вызывая конструктор. Но здесь есть несколько неприятных моментов:
- такой класс не может быть final
- лучше использовать настоящие объекты в тестах, вместо mock

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

Еще хуже выполнять в конструкторах IO операции, особенно сторонние вызовы в другие сервисы! Если это будет какой-нибудь Spring bean и произойдет исключение (например, сервис не отвечает), то контекст попросту не сможет подняться, что чревато в принципе неработающим приложением в продакшене. Такие ситуации я уже не раз встречал на своем опыте, и это действительно печально, когда даже откатиться на предыдущие версию не поможет восстановить работосопособность приложения.

Поэтому, вместо того, чтобы добавлять логику в конструктор, лучше перенести ее в какие-нибудь фабричные методы/классы, или вообще попросить у Dependency Injection framework нужные тебе параметры.

#dmdev_best_practices
👍88🔥2613
👍5
Когда литералы выносить в константы

Литералы, такие как строки или числа (более подробно рассказывал про них в курсе Computer Science) можно либо просто оставлять в коде как есть, либо выносить их в константы и потом уже использовать именно константы.

В принципе, предпочтение нужно отдавать созданию констант, особенно если:
1. Одно и то же значение используется в нескольких местах, ибо это важно иметь one source of truth (иначе легко изменить значение в одном месте и забыть поменять в другом, из-за чего один и тот же функционал будет работать по-разному)
2. Значение требует пояснения, и гораздо лучше создать константы с хорошим названием и объяснением над ней, нежели оставлять неуклюжие комментарии прямо в месте использования литерала.

Например:

static final int GRACE_PERIOD_HOURS = 12;
scheduleSubscriptionRenewal(validUntil + GRACE_PERIOD_HOURS);

// или для сравнения:

scheduleSubscriptionRenewal(validUntil + 12);


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

Самые часто распространенные примеры, когда лучше оставлять литералы:
- true, false
- 0, 1
- SQL queries

#dmdev_best_practices
👍53🔥25❤‍🔥43👏1
Хороший вопрос! Сейчас подробно отвечу...