Топ 19 кращих практик написання будь-якої програми на java

Оскільки я вже мало пишу кода, хочеться зберегти свій досвід, поки я його не забув. Отже, до уваги топ 19 кращих практик написання будь-якої програми на java.

Порядок топу довільний. Багато порад не є специфічними саме для java, але я їх теж включив до топу.

Щоб не розтягувати статтю, я не буду писати багато аргументації моїх порад. Так що цей топ для тих, хто мені довіряє.

1. Jvm-мови

Першою порадою буде використовувати саме java, а не інші jmv-мови, такі як: kotlin, Scala, Groovy тощо.

Ця порада витікає з моїх переконань того, як обирати технології для проекту. Java є найпопулярнішою jvm-мовою, вона має найкращу підтримку спільноти, в інтернеті є багато готових рішень. Цю ж логіку я буду використовувати в інших пунктах топу.

2. Версія java

Якщо у вас немає конкретних вимог до версії java, тоді раджу писати на тій версії java, що є найпопулярнішою в даний момент часу за версією google trends.

3. Система збірки

Раджу використовувати таку систему збірки, що є найпопулярнішою в даний момент часу за версією google trends.

Стосовно gradle. Зараз він модний, новіший, швидший. Але все ж він менш популярний, ніж maven, тому не раджу його до використання.

4. Середовище розробки

Якщо ви читаєте цю статтю в той час, коли російсько-українська війна закінчилася перемогою України з деокупацією всієї території України, тоді я раджу intellij idea community edition як середовище розробки. А поки цього не сталося, я раджу Visual Studio Code.

5. Назва програми

Назву програми, назву проекту, назву головного модулю раджу робити однаковими. Саму назву раджу робити у kebab-case форматі, це коли усі слова через дефіс з маленької літери.

6. Назва модулів програми

До назви модулів раджу додавати префікс з назвою програми. Наприклад, якщо програма називається super-program, тоді модулі називаються super-program-module1, super-program-module2 тощо.

7. Назва java-пакета

До офіційних правил неймінгу можу додати лише одне. Крайній пакет раджу назвати так само, як і назва програми, але без назви організації. Ну і без символів -, звісно. Наприклад, якщо програма називається company-name-super-program, тоді пакет називається com.companyname.superprogram.

8. Головний клас програми

Раджу завжди називати головний клас програми Main. Так його просто буде знайти.

Головний клас повинен робити тільки первинні налаштування та запустити справжню точку входу в програму. Більше нічого зайвого в головному класі не повинно бути.

Приклад головного класу програми:

package com.companyname.superprogram;

public class Main {
    public static void main(String[] args) {
        // первинні налаштування
        System.setProperty("user.timezone", "Asia/Kolkata");
        // точка входу в програму
        SuperProgram.start();
    }
}

9. Модулі

Під словом “модуль” я маю наувазі модуль системи збірки.

Розділяти java-програму на модулі я раджу тільки тоді, коли вам потрібно збирати різні jar-файли з різними частинами коду. Я не раджу розділяти код по модулям заради організації коду. Про організацію коду далі будуть інші поради.

10. Java-пакети

Якщо у вас невелика java-програма до 50 класів, тоді я не раджу розділяти класи по java-пакетам взагалі. Оріентуватися по коду простіше, коли усі класи в одному пакеті поряд з головним класом. Замість пакетів просто додавайте префікс до назви ваших класів.

Якщо у вас великий java проект більше 50 класів, тоді я раджу робити сервісну архітектуру. Про це піде мова в наступній пораді

11. Сервісна архітектура

Сервіс - це підпрограма всредині програми, яка відповідає за свою частину функціоналу. Технічно сервіс - це просто частина коду програми, який знаходиться в модулі з усім іншим кодом, але ми його розглядаємо, як окрему підпрограму.

Сервісна архітектура підходить для великих програм, які мають багато фунціоналу.

Сервісна архітектура вводить таку структуру пакетів:

com/companyname/programname
├── api/*.java
├── service/*/*.java
├── util/*.java
└── Main.java

Окремо про кожний пакет і файл:

  • Main.java - головний клас.
  • service/*/*.java - класи сервісів. Кожен сервіс має додатковий внутрішній пакет в пакеті service. Всі класи сервіса знаходяться всередині свого пакета. Класи сервіса не повинні додатково розділятися по пакетам.
  • util/*.java - спільні статичні класи-утиліти, що використовуються в декількох сервісах. Ці класи не повинні додатково розділятися по пакетам.
  • api/*.java - гарно задокументовані api-класи. Ці класи використвується для взаємодії між сервісами або між модулями програми. Якщо програма є open-source, тоді ці класи є її api. Ці класи не повинні додатково розділятися по пакетам.

Приклад сервісу:

com/companyname/programname/service/exampleservice
├── Listener.java
├── Timer.java
└── ExampleService.java

Як і у звичайної програми, у сервіса є головний клас ExampleService.java, який є точкою входу сервіса. Цей головний клас запускає весь код сервіса.

Головний клас програми запускає усі головні класи сервісів в правильному порядку:

package com.companyname.superprogram;

import com.companyname.superprogram.exampleservice1.ExampleService1;
import com.companyname.superprogram.exampleservice2.ExampleService2;

public class Main {
    public static void main(String[] args) {
        // первинні налаштування
        System.setProperty("user.timezone", "Asia/Kolkata");
        // запускаємо сервіси
        ExampleService1.main(args);
        ExampleService2.main(args);
    }
}

У сервісів є одне правило: класам сервіса заборонено звертатися напряму до класів інших сервісів. Сервіси повинні взаємодіяти через api-класи так, ніби вони реально різні програми, які спілкуються через якийсь там RMI. Це можна реалізуватися завдяки інтерфейсам, інкапсуляції, dto-класів тощо.

12. spring-context

Якщо ви вирішили зробити сервісну архітектуру, тоді її можна прокачати за допомогою фреймворку spring-context’а. Цей фреймворк може зробити автоматичне впровадження залежностей та деякі інші фішки. Хто не шарить, раджу прошаритися за прочитати про spring-context.

13. lombok

Використовуйте lombok. Він покращує java автогенерацією одноманітного коду та приховує його.

З мого найулюбленішого - це ігнорування checked-exceptions анотацією @SneakyThrows. І вам самим не раджу ніде використовувати checked-exceptions.

14. Модифікатор доступу класів

Для класів використовуйте модифікатор доступу тільки public. Не потрібно робити класи приватними, навіть якщо ви їх не використовуєте ззовні. Приватний модифікатор доступу - це лише зайва перешкода для комфортного дебагу.

Звісно, якщо стоїть реальне питання безпеки, тоді модифікатори доступу класів потрібні. Хто знає, той знає.

15. Кращі бібліотеки

Список кращих бібліотек, які потрібні в будь-якому проекті:

  • fastutil, google guava - швидкі колекції для примитивів і не тільки.
  • commons-lang3, commons-io, commons-math3 - утиліти, що збагачують стандартну бібліотеку java.
  • jsoup - веб запити.

Якщо потрібні з’єднання з базою даних:

  • HikariCP, jooq - кращі біліотеки для з’єднань з базами даних.

Якщо потрібні з’єднання з redis:

  • jedis-wrapper - мій форк бібліотеки jedis, біліотека для з’єднань з redis.

16. Оптимізація

Не оптимізуйте код передчасно. Замість цього дочекайтеся, коли програма дійсно почне лагати. Коли це сталося, аналізуйте навантаження за допомощою програм java mission control, visualvm. Втечі пам’яті додатково аналізуйте через eclipse memory analyzer.

17. Xms=Xmx

Не робіть Xms=Xmx. Збільшення продуктивності від цього копійочне, ви тільки оперативну пам’ять собі поріжете.

18. Великі класи

Якщо у вас клас збільшується в розмірі більше 150 строк коду, а ви гадки не маєте, як цей код зменшити чи розділити, тоді у мене є лайфхак. Просто створіть ще один статичний клас поряд, назвіть його так само, як і ваш клас, тільки додайте слово Utils в кінець. Та просто винесіть в цей клас найбільші методи з вашого класу. Так само туди можна винести просто негарні методи, які вам око ріжуть.

Наприклад:

public class SigmaClass {
    public void bigMethod(String[] args) {
        SigmaClassUtils.bigMethod0(args); // викликаємо код великого методу
    }

    public void normalMethod(String[] args) {
        // тіло невеликого методу
    }
}

public class SigmaClassUtils {
    public static void bigMethod0(String[] args) {
        // 100 строк великого методу SigmaClass.bigMethod
    }
}

19. Події

Події - це такий патерн програмування, коли ви надаєте свій код іншій бібліотеці з розрахунком, що вона викличе ваш код, коли станеться якась подія. Я не раджу довіряти подіям.

Якщо ви створили подію, тоді вам потрібно зробити такий додатковий захисний код:

  1. Код, що захистить від того, що подія не викликалася.
    Щоб захиститися від цього, раджу створити таймер, який буде перевіряти стан об’єктів вашої програми. Якщо якийсь об’єкт в неправильному стані, тоді виправляти його. Наприклад, користувач закрив вікно, але подія закриття вікна не викликалася, через що в ваш код все ще про це не знає.
  2. Код, що захистить від того, що подія викликалася двічі.
    В коді слухача події перевіряйте, чи точно ця подія ще не викликалася. Якщо знову обробити таку подію, то у вашому коді може порушитися узгодженість. Наприклад, користувач закрив вікно, яке вже закривалося.
  3. Код, що обробляє помилки вашого коду. Подія не завжди коректно обробляє помилки.
    В код слухача події вставте глобальний блок try { ... } catch (Exception e) { e.printStackTrace(); }.
  4. Код, що захистить від неправильного порядку виклику подій.
    В коді слухача події перевіряйте, чи точно ця подія викликалася правильно. Якщо обробити неправильну подію, то у вашому коді може порушитися узгодженість. Наприклад, користувач закрив вікно перед тим, як воно відкрилося.

Настав час внести правки в пункт 4. Середовище розробки.