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
Названия тестов - очень важны!

Название теста должно описывать поведение (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
Хороший вопрос! Сейчас подробно отвечу...
Forwarded from Dmitry Alekhine
Денис, в энтерпрайз проге существует куда более масштабная проблема, я бы хотел услышать твое мнение по поводу нее:

Существует тонна библиотек и фреймворков, каждый(ая) решает проблему на все случаи жизни

Увы, наблюдаю, что в самом большом софте, будь то даже blink engine (движок хрома) - челы не используют stdlib, другие либы - они пишут свои собственные реализации корутин, виртуальных тредов, пулов и так далее.

С чем это скорее всего связано:
> Многоуровневый оберточный пи*дец "для удобства"
> Нежелание разобраться с библиотекой: "я свою реализацию быстрее напишу" - именно это больше всего и влияет на observability проекта и на его overbloat'ность

С другой стороны есть такие гении, которые ради одной функции импортируют целый фреймворк. Открываешь POMник - 500+ строк

Как находить баланс при разработке проекта, чтобы проект медленнее скатывался в оверблоат, но и не был тяжеловесным говном благодаря ленивым и глупым васянам, которые не в силах помимо чейнинга фремворочков и жсончиков придумать свою реализацию
🔥22👍5❤‍🔥31
Ответ на вопрос 👆

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

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

Далее идет использование инструмента другими разработчиками и их онбординг в проект/компанию. Если это общеизвестная библиотека или фреймворк вроде Spring, Hibernate, которые еще и обновляются “сами” - то с очень большой вероятностью человек будет знать его, что сэкономит время, а значит и деньги для компании.

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

Пример с хромом и другими продуктами от гигантов вроде Google, Amazon и т.д. не стоит брать во внимание, потому что у них штат сотрудников в десятки и сотни тысяч человек. У них практически все используется свое самописное, потому что для них так проще. У них есть достаточно денежных и человеческий ресурсов, чтобы не только создавать такие инструменты, но и поддерживать их и даже выкладывать в open source. И онбординг в 3-6 месяцев для них ничего не стоит.
👍64🔥20❤‍🔥21👏1
Операции должны быть идемпотентны

Идемпотентность означает, что выполнение одного и того же запроса несколько раз подряд приведет к тому же результату, что и выполнение этого запроса в первый раз. Сюда относятся любые операции и по любым протоколам, которые использует сервис для обработки внешних запросов. Например, REST API, gRPC, Thrift, Message Brokers (Kafka, Cloud Pub/Sub) и т.д.

Почему это так важно?

1️⃣ Идемпотентные запросы могут предотвратить случайное выполнение повторных операций, что может привести к ошибкам или нежелательным изменениям в системе. Обычно из приведенных протоколов общения - никто не гарантирует, даже Message Brokers, что одно и то же сообщение будет доставлено ровно один раз!

2️⃣ Идемпотентные запросы обеспечивают надежность операций, позволяя клиентам повторять запросы в случае неудачи или потери связи.

3️⃣ Идемпотентные запросы облегчают работу с API, так как клиенты могут повторять запросы без необходимости дополнительной логики для управления состоянием. Другими словами говоря, клиенту не стоит переживать и усложнять свою систему для отправки запросов сервису.

Если брать идемпотентность в контексте CRUD операций, про которые я довольно много рассказывал в курсе Spring:

- Read операции (HTTP GET) по умолчанию должны быть идемпотентными. Ибо никаких изменений на сервере не должно происходить при обработки запросов на чтение данных.

- Update операции (HTTP PUT) точно также легко сделать идемпотентными. Можно сколько угодно обновлять один и тот же объект с теми же самыми полями в запросе.

- Delete операции (HTTP DELETE) чуть сложнее, но удаление одного и того же ресурса не должно вызывать ошибки на сервере, если такого ресурса не оказалось во втором и последующих запросах

- Create операции (HTTP POST) сложнее всего добиться идемпотентности, поскольку их выполнение приводит к созданию нового ресурса с уникальным идентификатором. Повторный запрос на создание ресурса с теми же параметрами может привести к созданию дубликатов или ошибкам. Но все-таки способы есть, про которые я расскажу в отдельном посте!

#dmdev_best_practices
👍76🔥27❤‍🔥72