Neueste Beiträge von Джавист Роман ☕️ (@romankh3) auf Telegram

Джавист Роман ☕️ Telegram-Beiträge

Джавист Роман ☕️
Авторский канал Java, Kotlin разработчика с более, чем 9-летним опытом.

GitHub: https://github.com/romankh3
Библиотека: https://t.me/romankh3books
Чат: https://t.me/romankh3_chat
1,796 Abonnenten
43 Fotos
3 Videos
Zuletzt aktualisiert 06.03.2025 10:08

Der neueste Inhalt, der von Джавист Роман ☕️ auf Telegram geteilt wurde.

Джавист Роман ☕️

05 Dec, 05:33

1,802

начало 👆👆

Почему всё равно хочется это сделать?


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

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

---

Когда это действительно уместно?


Даже у Util-классов есть свои моменты, когда их использование вполне оправдано. Вот несколько примеров:
1. Чисто функциональные задачи: преобразование данных в очень простых сценариях. Например, класс для хэширования паролей.
2. Маленькие проекты или скрипты: когда проект небольшой и вероятности роста почти нет. Здесь можно пожертвовать правильной архитектурой ради скорости.
3. Вспомогательные библиотеки: методы общего назначения для вашего проекта, которые четко отделены в отдельный модуль. Например, Apache Commons или Guava в Java.

---

Заключение

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

Если так уж невмоготу, то можно подумать еще над тем, чтобы заиспользовать уже известные common библиотеки, они хотя бы уже протестированы нормальным образом как разработчиками, так и пользователями.
Джавист Роман ☕️

05 Dec, 05:33

1,406

Util-классы и методы: почему они вредят и засоряют код?

Продолжаем холиварить, теперь поговорим про такие нужные и важные util классы/методы 😈

Почти каждый разработчик на определенном этапе прибегал к созданию Util-классов или методов вроде StringUtil, DateUtil, CommonUtil и прочих. На первый взгляд, это выглядит удобно: все "вспомогательные" функции в одном месте! Но на практике такой подход не только создает технический долг, но и приводит к загрязнению кода. Давайте разберем, почему это плохо, на 5 конкретных пунктах с примерами.

---

1️⃣ Нарушение принципа единой ответственности (SRP)

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

Пример:
public class StringUtil {
public static int countWords(String input) { /*...*/ }
public static String reverse(String input) { /*...*/ }
public static boolean isPalindrome(String input) { /*...*/ }
public static LocalDateTime parseDate(String date) { /*...*/ } // Зачем здесь разбор дат?
}

Этот Util-класс делает все подряд и никак не сфокусирован на одной задаче.

Пусть даже мы скажем, что нет нет, мы разделим эти методы по смыслам, в таком случае у нас получится хаос и анархия не только в одном месте, но еще и в нескольких :D



2️⃣ Нет четкой связанности (Cohesion)

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

Плохой пример:
public class CommonUtil {
public static double calculateTax(double income) { /*...*/ }
public static String formatString(String input) { /*...*/ }
public static boolean sendEmail(String email) { /*...*/ }
}


Какая связь между расчетом налогов, форматированием строки и отправкой почты? Никакой.

---

3️⃣ Сложность внесения изменений и масштабирования

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

Пример:
Кто-то добавил новый метод в DateUtil
public static String getTodayAsString() { 
return LocalDate.now().toString();
}
// Но вот проблема: имя метода не отражает, в каком формате возвращается дата. Как итог — путаница.


---

4️⃣ Неочевидное использование и дублирование кода

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

Пример:
Многие проекты заканчиваются такими дублями:
- DateUtil
- TimeUtil
- DateTimeUtil

Все три класса решают примерно одни и те же проблемы, но используются разными модулями.

---

5️⃣ Трудности с внедрением зависимостей и тестированием

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

Пример:

public static String generateUUID() {
return UUID.randomUUID().toString();
}

Если метод статический, в тестах его подменить будет невозможно без специальных фреймворков вроде PowerMock.

---

Продолжение далее👇👇
Джавист Роман ☕️

01 Dec, 13:36

1,615

Почему писать бизнес-логику в SQL-запросах — плохая идея?

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

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

Вот 5 основных причин, почему это не рекомендуется:

1️⃣ Сложность поддержки и отладки

SQL-запросы, содержащие сложную логику, становятся трудно читаемыми и практически невозможно разобраться, что где работает неправильно. Чем больше там логики, тем сложнее её тестировать и исправлять ошибки.

2️⃣ Нет повторного использования кода

SQL, как правило, плохо подходит для повторного использования кода. Если логику нужно применить или изменить в другом месте, её придется копировать, что создаёт избыточность и увеличивает количество «точек отказа».

3️⃣ Перенос сложности в неподходящий слой

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

4️⃣ Сложности масштабирования

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

5️⃣ Трудности работы с несколькими хранилищами данных

Если ваша система использует не одну базу данных (например, SQL + NoSQL), размещение логики в SQL усложняет интеграцию и синхронизацию данных между разными система.

Когда можно писать бизнес-логику в SQL?

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

Пишите код грамотно и помните: бизнес-логика должна быть там, где ей место — в приложении. 😉

#аксиомаПрограммирования #sql
Джавист Роман ☕️

12 Oct, 12:31

2,377

Начало 👆👆

## Расположение
Тесты должны располагаться в отдельной папке, обычно это src/test/java в проектах Maven или Gradle.
Структура директорий должна дублировать структуру основного кода, чтобы было легко найти тесты, соответствующие классу.

UPDATE: спасибо подписчику, указал на несомненно более важную причину: в таком расположении будут доступны package-private классы, методы и переменные. Без этого будет значительно сложнее писать тесты.

## Советы, как делать

### Принцип по написанию тестов FIRST
Принципы написания тестов, известные под акронимом FIRST, помогают разработчикам создавать качественные и эффективные тесты. FIRST расшифровывается как:

#### Fast (Быстрые)
Тесты должны выполняться быстро, чтобы их можно было запускать часто и без значительных затрат времени.
Это позволяет разработчикам получать обратную связь об изменениях кода как можно быстрее.
Если тесты будут идти долго, то время деливери продукта будет сильно сдвинуто.

#### Isolated (Изолированные)
Каждый тест должен быть независимым от других. Это значит, что результаты одного теста не должны зависеть от результатов другого. Изолированные
тесты проще отлаживать и поддерживать, так как проблемы можно точно локализовать. Другими словами, связанный тест - это неправильно
написанный тест, который создаст намного больше хлопот, чем пользы, для которой разработчик решит связать тесты.

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

#### Self-Validating (Автоматически проверяемые)
Тесты должны иметь четкие результаты: либо проходят, либо нет. Это упрощает процесс проверки,
так как разработчики не должны вникать в результаты тестов — они просто видят, прошел ли тест или нет.

#### Timely (Своевременные)
Тесты должны писаться как можно раньше в процессе разработки, желательно сразу после написания кода или даже до него(в практике тестируемого
разработки, например, TDD — тестирование перед разработкой). Это помогает предотвратить ошибки и обеспечивает более высокое качество кода.

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

### А также

- Пишите короткие и понятные тесты, проверяющие одну вещь. Это повысит их читаемость и простоту в отладке.
- Используйте методы для общих проверок (assertEquals, assertTrue и т. д.) из библиотеки утверждений.
- Не забывайте покрывать крайние случаи и исключения.
- Используйте моки для интеграции с зависимыми компонентами (например, Mockito).

## Вместо вывода
Написание тестов это такая неотъемлемая часть разработки, как и написание бизнес логики. Стоимость поиска дефекта на этапе разработки
через тесты, написанные разработчиком на порядок дешевле от стоимости решения дефекта, который нашли в продакшене.

Всем добра и мирного неба над головой
Джавист Роман ☕️

12 Oct, 12:30

2,006

Начало 👆👆

## Именование
В тестах для разработки есть два вида тестов - интеграционных и модульные. Как их именовать?

### Классы
Возьмем к примеру класс PluggableAppAdministrationApi, для него могут быть как интеграционные, так и модульные тесты.
Для интеграционного теста к названию класса нам нужно добавить суффикс IT, для модульного просто Test:

- PluggableAppAdministrationApi -> PluggableAppAdministrationApiIT - интеграционный тест
- PluggableAppAdministrationApi -> PluggableAppAdministrationApiTest- модульный тест

### Методы
Название теста должно быть понятным и отражать его суть.
Используйте название метода, который тестируется, в сочетании с ожидаемым результатом или поведением.

Например: shouldReturnTrueWhenInputIsValid.

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

Структура тестов через BDD (Behavior Driven Development) позволяет разработчикам, тестировщикам и другим заинтересованным сторонам легко
понимать требования и сценарии поведения системы. Вот основные компоненты, которые помогут разработчикам правильно писать тесты в формате BDD:

- Сценарий - это название теста, каждому сценарию должно быть дано четкое название, описывающее его цель.
- Given (Дано): Устанавливает предысторию и состояние системы. Определяет, из какого состояния начинается тест.
- When (Когда): Описывает действие или событие, которое инициирует поведение системы.
- Then (То): Определяет, какое поведение ожидается от системы после выполнения действия. Это описание ожидаемого результата.
- And (И): Используется для добавления дополнительных условий или шагов для более сложных сценариев.

Пример такого теста из одной библиотеки:

    @DisplayName("The most important test. Shown, that the changes in algorithm, "
+ "don't break the main behaviour and result as expected")
@Test
public void shouldProperlyExecuteComparing() {
//given
BufferedImage expectedResultImage = readImageFromResources("result.png");

File file = new File("build/test-images/result.png");

//when
ImageComparison imageComparison = new ImageComparison("expected.png", "actual.png");
ImageComparisonResult imageComparisonResult = imageComparison.compareImages().writeResultTo(file);

//then
assertNotNull(imageComparison.getActual());
assertNotNull(imageComparison.getExpected());
assertEquals(MISMATCH, imageComparisonResult.getImageComparisonState());
assertImagesEqual(expectedResultImage, imageComparisonResult.getResult());
}


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

Продолжение 👇👇👇
Джавист Роман ☕️

12 Oct, 12:30

1,837

🚀 Мысли о том, как писать тесты разработчику 🚀

Всем привет, будущие senior java developers! Давно мы с вами не виделись.

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

Заранее спасибо вам)

Ну, не буду дальше тянуть, поехали!

Далее 👇👇
Джавист Роман ☕️

30 Nov, 09:59

4,349

Сколько должен длиться онбоардинг
Джавист Роман ☕️

30 Nov, 09:55

3,910

Как правильно сделать онбоардинг
Джавист Роман ☕️

30 Nov, 09:50

3,604

Тезисы по проведению 1-2-1
Джавист Роман ☕️

30 Nov, 08:21

3,465

Как стать эффективным менеджером