Какой основной тестовый фреймворк у вас на проекте?
Anonymous Poll
25%
JUnit 4
57%
JUnit 5
6%
TestNG
11%
Что-то другое
JUnit, часть 1: джентельменский набор
Список фич, которые пригодятся для большинства тестов. Если что-то не видели - почитайте, вдруг пригодится. У JUnit чудесная документация, так что подробно расписывать не буду.
1️⃣ Жизненный цикл теста
Каждый тест - это метод с аннотацией
Через аннотацию
Чтобы выполнить что-то до или после выполнения теста, используются методы с аннотациями
Благодаря этому тесты выполняются независимо.
Этим JUnit отличается от TestNG, где создаётся один экземпляр класса на все тестовые методы. Если хочется как в TestNG, добавьте над классом аннотацию
Сердце каждого теста - методы с приставкой assert*:
3️⃣ Группировка тестов
Аннотация
Можно указывать тэги в системе сборки и при запуске тестов из IDE.
4️⃣ Отключение тестов
Аннотация
▫️ операционной системы
Помогают запустить один тест с разными аргументами. Выглядит так:
Вместо готового списка можно брать значения
🔸 из CSV файла
▫️ Через ассерт
▫️ Hamсrest, AssertJ - расширенные библиотеки методов-ассертов
▫️ Mockito для заглушек. Добавляете библиотеку в pom.xml или build.gradle, а в тест - аннотацию
▫️ Java Faker - генератор данных для тестов
Список фич, которые пригодятся для большинства тестов. Если что-то не видели - почитайте, вдруг пригодится. У JUnit чудесная документация, так что подробно расписывать не буду.
1️⃣ Жизненный цикл теста
Каждый тест - это метод с аннотацией
@Test
. Через аннотацию
@DisplayName
задаётся симпатичное имя теста в отчёте.Чтобы выполнить что-то до или после выполнения теста, используются методы с аннотациями
▫️ @Before, @BeforeAllJUnit создаёт новый экземпляр класса на каждый тестовый метод. Класс
▫️ @After, @AfterAll
ServiceTest
с пятью методами @Test
во время запуска превратится в 5 экземпляров класса ServiceTest
.Благодаря этому тесты выполняются независимо.
Этим JUnit отличается от TestNG, где создаётся один экземпляр класса на все тестовые методы. Если хочется как в TestNG, добавьте над классом аннотацию
@TestInstance(Lifecycle.PER_CLASS)
2️⃣ ПроверкиСердце каждого теста - методы с приставкой assert*:
🔸 assertTrueВ самом JUnit мало методов, более удобные ассерты есть в библиотеках Hamсrest и AssertJ. AssertJ, на мой взгляд, более читабельный, но Hamсrest используется чаще.
🔸 assertEquals
🔸 assertInstanceOf
3️⃣ Группировка тестов
Аннотация
@Tag("groupName")
объединяет тесты в группы. Работает и для одного теста, и для класса.Можно указывать тэги в системе сборки и при запуске тестов из IDE.
4️⃣ Отключение тестов
Аннотация
@Disabled
. Продвинутые варианты для: ▫️ операционной системы
@DisabledOnOs(WINDOWS)▫️ версии java
@DisabledOnJre(JAVA_9)▫️ системных переменных:
@DisabledForJreRange(min = JAVA_9)
@DisabledIfSystemProperty(named = "ci-server", matches = "true")5️⃣ Параметризированные тесты
@DisabledIfEnvironmentVariable(named = "ENV", matches = ".*development.*")
Помогают запустить один тест с разными аргументами. Выглядит так:
@ParameterizedTestТакой тест запустится дважды - с аргументом 100 и -14.
@ValueSource(ints={100,-14})
public void test(int input) {}
Вместо готового списка можно брать значения
🔸 из CSV файла
@CsvSource
🔸 из метода @MethodSource
6️⃣ Проверка таймаута▫️ Через ассерт
assertTimeout(ofMinutes(2), ()->{});▫️ Через аннотацию
@Timeout(value=42,unit=SECONDS)7️⃣ Полезные библиотеки
▫️ Hamсrest, AssertJ - расширенные библиотеки методов-ассертов
▫️ Mockito для заглушек. Добавляете библиотеку в pom.xml или build.gradle, а в тест - аннотацию
@ExtendWith(MockitoExtension.class)
▫️ Testcontainers для запуска внешних компонентов в докере. Добавляем библиотеку, аннотацию @Testcontainers
над классом и @Container
над компонентом▫️ Java Faker - генератор данных для тестов
JUnit, часть 2: зачем всё менять?
В JUnit сложилась странная ситуация.
JUnit 4 - топовый java фреймворк для тестов, который используется более чем в 100 тысячах проектов.
JUnit 5 вышел спустя 10 лет и обещает много нового. Но нельзя просто взять и перейти на 5 версию:
▪️ Аннотации вообще другие
▪️ Зависимостей стало больше
▪️ Для совместимости с JUnit 4 надо подключать какой-то винтаж.
Обратная совместимость - обычное дело для java приложений. В новой версии всё работает как раньше. Постепенно добавляешь новые фичи.
Почему JUnit не пошёл по этому пути?
Чтобы ответить на вопрос, нужно копнуть в архитектуру и кейсы использования.
JUnit создавался как удобная библиотека для написания тестов. Для разработчика есть API - аннотации
Дальше эти тесты запускает IDE или система сборки.
И вот им приходится тяжело. В JUnit 4 API для запуска и анализа тестов очень ограниченный, поэтому IDE и сборщики используют рефлекшн и другие обходные пути.
Чем плох такой подход - понятно. Любое изменение внутренней реализации ломает логику внутри IDE/системы сборки.
JUnit 5 учёл эту проблему и содержит три отдельных артефакта:
🔸 Jupiter - апи для разработчиков
🔸 Platform - апи для запуска и анализа тестов. Целевая аудитория - IDE, плагины и системы сборки. Теперь каждый из них может использовать библиотеку, а не писать свой велосипед
🔸 Vintage - для запуска JUnit 4 тестов на новой платформе
Почему у JUnit 4 и 5 разные аннотации?
У JUnit 5 абсолютно другая кодовая база. Для совместимости с 4 версией пришлось бы наворотить много кода. Гораздо практичнее вынести все адаптеры в отдельный компонент.
Тогда
▫️ Старые тесты будут работать
▫️ Чётко видно, где старые тесты, а где новые. А значит есть шанс, что со временем кодовая база с тестами перейдёт на новую версию.
Что здесь особенного?
В целом выглядит как обычный рефакторинг. Продукт развивается, мир меняется, монолит делится на составные части.
Но в этой истории есть две важные детали.
1️⃣ На страничке принципов разработки команды JUnit есть такие строки:
▫️ JUnit has never tried to be a swiss army knife
▫️ Third party developers move more quickly than we do
Отсюда видна ещё одна мотивация: поощрение развития других библиотек и фреймворков.
Другие разработчики тестовых библиотек теперь могут использовать JUnit платформу и автоматически получать поддержку библиотек во всех IDE и системах сборки.
2️⃣ Вторая инициатива команды JUnit - проект Open Test Alliance for the JVM.
В чём суть: есть много тестовых фреймворков и библиотек. Все они работают по-разному - бросают разные исключения, отличается формат и набор данных и тд. IDE и системам сборки приходится учитывать все особенности.
Идея проекта - создать общую спецификацию для тестовых библиотек. Проект поддержали TestNG, Spock, Hamcrest, AssertJ, Eclipse, IntelliJ, Gradle, Maven и Allure.
Неизвестно, закончится ли эта история удачно, но идея классная.
Здорово, когда компания делает не только хороший продукт, но и способствует развитию отрасли в целом😇
В JUnit сложилась странная ситуация.
JUnit 4 - топовый java фреймворк для тестов, который используется более чем в 100 тысячах проектов.
JUnit 5 вышел спустя 10 лет и обещает много нового. Но нельзя просто взять и перейти на 5 версию:
▪️ Аннотации вообще другие
▪️ Зависимостей стало больше
▪️ Для совместимости с JUnit 4 надо подключать какой-то винтаж.
Обратная совместимость - обычное дело для java приложений. В новой версии всё работает как раньше. Постепенно добавляешь новые фичи.
Почему JUnit не пошёл по этому пути?
Чтобы ответить на вопрос, нужно копнуть в архитектуру и кейсы использования.
JUnit создавался как удобная библиотека для написания тестов. Для разработчика есть API - аннотации
@Test
, @Before
, методы assertEquals
и тд. Здесь всё классно.Дальше эти тесты запускает IDE или система сборки.
И вот им приходится тяжело. В JUnit 4 API для запуска и анализа тестов очень ограниченный, поэтому IDE и сборщики используют рефлекшн и другие обходные пути.
Чем плох такой подход - понятно. Любое изменение внутренней реализации ломает логику внутри IDE/системы сборки.
JUnit 5 учёл эту проблему и содержит три отдельных артефакта:
🔸 Jupiter - апи для разработчиков
🔸 Platform - апи для запуска и анализа тестов. Целевая аудитория - IDE, плагины и системы сборки. Теперь каждый из них может использовать библиотеку, а не писать свой велосипед
🔸 Vintage - для запуска JUnit 4 тестов на новой платформе
Почему у JUnit 4 и 5 разные аннотации?
У JUnit 5 абсолютно другая кодовая база. Для совместимости с 4 версией пришлось бы наворотить много кода. Гораздо практичнее вынести все адаптеры в отдельный компонент.
Тогда
▫️ Старые тесты будут работать
▫️ Чётко видно, где старые тесты, а где новые. А значит есть шанс, что со временем кодовая база с тестами перейдёт на новую версию.
Что здесь особенного?
В целом выглядит как обычный рефакторинг. Продукт развивается, мир меняется, монолит делится на составные части.
Но в этой истории есть две важные детали.
1️⃣ На страничке принципов разработки команды JUnit есть такие строки:
▫️ JUnit has never tried to be a swiss army knife
▫️ Third party developers move more quickly than we do
Отсюда видна ещё одна мотивация: поощрение развития других библиотек и фреймворков.
Другие разработчики тестовых библиотек теперь могут использовать JUnit платформу и автоматически получать поддержку библиотек во всех IDE и системах сборки.
2️⃣ Вторая инициатива команды JUnit - проект Open Test Alliance for the JVM.
В чём суть: есть много тестовых фреймворков и библиотек. Все они работают по-разному - бросают разные исключения, отличается формат и набор данных и тд. IDE и системам сборки приходится учитывать все особенности.
Идея проекта - создать общую спецификацию для тестовых библиотек. Проект поддержали TestNG, Spock, Hamcrest, AssertJ, Eclipse, IntelliJ, Gradle, Maven и Allure.
Неизвестно, закончится ли эта история удачно, но идея классная.
Здорово, когда компания делает не только хороший продукт, но и способствует развитию отрасли в целом😇
Как вы думаете, во сколько обошлась переделка архитектуры JUnit и написание практически новой кодовой базы?
Anonymous Poll
11%
2 тысячи евро
13%
25 тысяч евро
29%
100 тысяч евро
32%
5 миллионов евро
14%
20 миллионов евро
JUnit, часть 3: модели кастомизации
Изменение архитектуры - не всё, чем JUnit 5 отличается от предыдущей версии. Второе отличие касается модели кастомизации.
В этом посте поговорим, зачем это нужно в тестовом фреймворке, и про разницу между 4 и 5 версией.
Чтобы было понятнее, давайте опишем простую задачу и будем её понемногу усложнять.
Допустим, нужно измерить время выполнения каждого теста: запустить таймер вначале и вывести время выполнения в конце.
Для одного класса это несложно - просто добавляем методы с аннотациями
А как посчитать время для всех классов? Здесь варианта два:
🔸 Вынести общий код в отдельный класс, в каждый класс-тест добавить методы Before и After. Решение рабочее, но придётся копипастить методы в каждый класс.
🔸 Внедрить логику где-то на верхнем уровне и включать/выключать её через настройки или аннотации.
Это и есть кастомизация - предусмотренные библиотекой места "встраивания" новой логики. JUnit 4 и 5 используют для этого разные механизмы. Давайте кратко их обсудим.
JUnit 4 Runner
Переопределяем жизненный цикл теста целиком. Наследуемся от интерфейса
Примеры:
▫️
▫️
▫️
▫️
Главный минус - жизненный цикл только один, значит
JUnit 4 Rule
Переопределяем интерфейс
▪️
▪️
Плюсы-минусы:
✅ Можно использовать несколько rule в одном классе
❌ Работает в рамках одного метода и по сути похож на before/after.
JUnit 5 Extension
Жизненный цикл теста разбивается на 10+ фаз. К каждой из них можно присоединиться, если переопределить нужный интерфейс:
Реализуем нужные интерфейсы, регистрируем класс и готово. Похожий механизм используется в Spring.
✅ Класс может использовать несколько экстеншенов
✅ Можно вклиниться на любых этапах жизненного цикла
✅ В интерфейсах доступен контекст выполнения и вся информация про тесты, в итоге возможностей гораздо больше
В JUnit 5 полностью убрали поддержку
______
Разбирать чужие кейсы полезно, но не всегда увлекательно. Поэтому вот интересный факт про разработку JUnit.
JUnit - опенсорсный проект, где никто никому не платил за работу.
Но рефакторинг назревал много лет. Однажды ребята решили, что такие грандиозные планы требуют фулл тайм и объявили краудфандинг на JUnit 5.
Сумма требовалась небольшая - 25 тысяч евро, меньше двух миллионов рублей. В итоге собрали в 2 раза больше, и уже через 6 недель был готов первый прототип.
Меня это очень впечатляет, особенно в сравнении со стоимостью и скоростью разработки в энтерпрайзе🙈
Изменение архитектуры - не всё, чем JUnit 5 отличается от предыдущей версии. Второе отличие касается модели кастомизации.
В этом посте поговорим, зачем это нужно в тестовом фреймворке, и про разницу между 4 и 5 версией.
Чтобы было понятнее, давайте опишем простую задачу и будем её понемногу усложнять.
Допустим, нужно измерить время выполнения каждого теста: запустить таймер вначале и вывести время выполнения в конце.
Для одного класса это несложно - просто добавляем методы с аннотациями
@Before
и @After
.А как посчитать время для всех классов? Здесь варианта два:
🔸 Вынести общий код в отдельный класс, в каждый класс-тест добавить методы Before и After. Решение рабочее, но придётся копипастить методы в каждый класс.
🔸 Внедрить логику где-то на верхнем уровне и включать/выключать её через настройки или аннотации.
Это и есть кастомизация - предусмотренные библиотекой места "встраивания" новой логики. JUnit 4 и 5 используют для этого разные механизмы. Давайте кратко их обсудим.
JUnit 4 Runner
Переопределяем жизненный цикл теста целиком. Наследуемся от интерфейса
Runner
или абстрактного класса, в нужных местах добавляем нужные действия. Теперь тесты запускаются не по стандартной схеме, а по той, что прописана в новом классе.Примеры:
▫️
@RunWith(Parameterized.class)
запускает параметризованные тесты▫️
@RunWith(Suite.class)
запускает наборы тестов▫️
@RunWith(SpringJUnit4ClassRunner.class)
добавляет спринговые активности до и после запуска теста▫️
@RunWith(MockitoJUnitRunner.class)
позволяет использовать заглушкиГлавный минус - жизненный цикл только один, значит
Runner
для теста может быть только один. Не получится совместить несколько фич, например, параметризованные тесты с заглушками.JUnit 4 Rule
Переопределяем интерфейс
TestRule
и задаём действие до и после выполнения теста. В тестах выглядит как просто поле:@RuleВ JUnit 4 есть несколько готовых правил:
public Timeout globalTimeout = Timeout.seconds(10);
▪️
TemporaryFolder
- создать временную папку для теста▪️
ExternalResource
- открыть и закрыть внешний ресурс(файл, сокет, БД)Плюсы-минусы:
✅ Можно использовать несколько rule в одном классе
❌ Работает в рамках одного метода и по сути похож на before/after.
JUnit 5 Extension
Жизненный цикл теста разбивается на 10+ фаз. К каждой из них можно присоединиться, если переопределить нужный интерфейс:
▫️BeforeAllCallback
- действие перед всеми тестами▫️ParameterResolver
- передача параметров в тестРеализуем нужные интерфейсы, регистрируем класс и готово. Похожий механизм используется в Spring.
✅ Класс может использовать несколько экстеншенов
✅ Можно вклиниться на любых этапах жизненного цикла
✅ В интерфейсах доступен контекст выполнения и вся информация про тесты, в итоге возможностей гораздо больше
В JUnit 5 полностью убрали поддержку
Runner
и Rule
, всё переписано на Extension API. Кодовые базы стали несовместимы между собой, поэтому и нужна библиотека Vintage с адаптерами.______
Разбирать чужие кейсы полезно, но не всегда увлекательно. Поэтому вот интересный факт про разработку JUnit.
JUnit - опенсорсный проект, где никто никому не платил за работу.
Но рефакторинг назревал много лет. Однажды ребята решили, что такие грандиозные планы требуют фулл тайм и объявили краудфандинг на JUnit 5.
Сумма требовалась небольшая - 25 тысяч евро, меньше двух миллионов рублей. В итоге собрали в 2 раза больше, и уже через 6 недель был готов первый прототип.
Меня это очень впечатляет, особенно в сравнении со стоимостью и скоростью разработки в энтерпрайзе🙈
Статистика по микросервисам
К концу года появляется много отчётов и статистики о состоянии JVM экосистемы. В целом ситуация не сильно отличается от прошлого года:
🔸 Самая популярная версия java до сих пор 8, используется на 69% проектов. Java 11 встречается на 36% проектов, а 12 и выше - на 16%.
🔸 Самый популярный фреймворк - Spring Boot: 65% проектов. На втором месте Spring MVC: 40% проектов.
🔸 Самая популярная IDE - Intellij IDEA, система сборки - Maven.
🔸 Трендовые GraalVM, Quarkus и Micronaut нашли применение где-то на 4% проектов, цифра примерно как в прошлом году.
Поэтому в этом посте поделюсь статистикой не по джаве, а по всему, что связано с микросервисами. Данные взяла из JRebel Java Developer Productivity Report 2021 и JetBrains The State of Developer Ecosystem 2021.
Демография: Россия - 3% участников, в основном ответы из США, Китая и Европы.
Итак,
49% проектов используют микросервисы
42% сидят с монолитом
10% применяют serverless архитектуру
На чём пишут микросервисы:
41% Java
37% JavaScript
25% Python
Сколько микросервисов на проекте:
34% Меньше пяти
36% 5-10
14% 10-20
16% 20 и больше
Как микросервисы общаются между собой?
83% HTTP-вызовы
47% Месседж брокер
25% WebSocket
20% RPC
С чем возникает много проблем:
30% Общение между сервисами
26% Масштабирование и производительность
24% Развернуть что-нибудь локально 🥲
14% Проблемы в коде микросервисов
Популярные performance проблемы:
54% Долгий Response Time
39% High CPU Usage
35% Утечки памяти
26% Много открытых соединений
Сколько времени занимает поднятие окружения на удалённой машине:
42% Меньше трёх минут
38% 3-10 минут
21% Дольше 10 минут
Только 20% разработчиков во время деплоя переключаются на другие рабочие задачи. Остальные 80% ходят за чаем, общаются с семьёй и листают соцсети🙂
К концу года появляется много отчётов и статистики о состоянии JVM экосистемы. В целом ситуация не сильно отличается от прошлого года:
🔸 Самая популярная версия java до сих пор 8, используется на 69% проектов. Java 11 встречается на 36% проектов, а 12 и выше - на 16%.
🔸 Самый популярный фреймворк - Spring Boot: 65% проектов. На втором месте Spring MVC: 40% проектов.
🔸 Самая популярная IDE - Intellij IDEA, система сборки - Maven.
🔸 Трендовые GraalVM, Quarkus и Micronaut нашли применение где-то на 4% проектов, цифра примерно как в прошлом году.
Поэтому в этом посте поделюсь статистикой не по джаве, а по всему, что связано с микросервисами. Данные взяла из JRebel Java Developer Productivity Report 2021 и JetBrains The State of Developer Ecosystem 2021.
Демография: Россия - 3% участников, в основном ответы из США, Китая и Европы.
Итак,
49% проектов используют микросервисы
42% сидят с монолитом
10% применяют serverless архитектуру
На чём пишут микросервисы:
41% Java
37% JavaScript
25% Python
Сколько микросервисов на проекте:
34% Меньше пяти
36% 5-10
14% 10-20
16% 20 и больше
Как микросервисы общаются между собой?
83% HTTP-вызовы
47% Месседж брокер
25% WebSocket
20% RPC
С чем возникает много проблем:
30% Общение между сервисами
26% Масштабирование и производительность
24% Развернуть что-нибудь локально 🥲
14% Проблемы в коде микросервисов
Популярные performance проблемы:
54% Долгий Response Time
39% High CPU Usage
35% Утечки памяти
26% Много открытых соединений
Сколько времени занимает поднятие окружения на удалённой машине:
42% Меньше трёх минут
38% 3-10 минут
21% Дольше 10 минут
Только 20% разработчиков во время деплоя переключаются на другие рабочие задачи. Остальные 80% ходят за чаем, общаются с семьёй и листают соцсети🙂
Intellij IDEA: редактирование кода
Несколько простых приёмов для быстрой работы.
Легендарные комбинации
Удалить всю строку:
Переместить выделенный код:
Несколько простых приёмов для быстрой работы.
Легендарные комбинации
Ctrl + Cработают со всей строкой, на которой стоит курсор, не нужно ничего выделять.
Ctrl + V
Ctrl + X
Удалить всю строку:
Ctrl + YДублировать строку:
Ctrl + DВыделить часть кода:
Ctrl + WПри каждом нажатии W захватывается всё большая область.
Переместить выделенный код:
Ctrl + Shift + ⬆️
Ctrl + Shift + ⬇️
L is for Liskov
— SOLID принципы знаете?
Думаю, нет разработчика, который не слышал на собеседовании такой вопрос.
Расшифровку знают многие. А вот практические знания часто ограничиваются Single Responsibility и Interface Segregation.
Как применять остальные буквы в ежедневной работе — не всегда понятно. Больше всего вопросов возникает насчёт L — Liskov Substitution Principle. О нём и будет сегодняшний пост.
Полное определение звучит так:
Пусть q(x) является свойством верным относительно объектов x некоторого типа T. Тогда q(y) также должно быть верным для объектов y типа S, где S является подтипом типа T.
По-простому: если заменить класс А на подкласс B, то система будет работать корректно и без неожиданных сайд-эффектов.
Как это выглядит на практике:
1️⃣ Выходной тип метода в наследнике такой же как у родителя или расширенный
Базовый класс:
Базовый класс:
3️⃣ Типы входных параметров те же или менее строгие. Пункт для общего понимания, тк для Java это неприменимо
Базовый класс:
4️⃣ Метод подкласса делает то же, что и метод базового класса
Базовый класс: метод
Наследник:
✅ Считает пользователей чуть по-другому
❌ Считает пользователей, обновляет статистику, сохраняет результат в БД
5️⃣ Метод наследника взаимодействует с теми же сущностями:
▪️ Метод родителя увеличивает счётчик - подкласс тоже увеличивает
▪️ Метод родителя не меняет поле - подкласс тоже не меняет
▪️ Метод родителя вызывает другие методы в определённом порядке - подкласс делает то же самое
А что можно вообще?
Если в подклассе объявлены новые поля, то методы подкласса могут делать с ними что угодно. На этом всё🙂
Правила выше - очень строгие. Но и наследование — штука непростая, это самая сильная связь между сущностями. Часто единственный плюс — это краткость кода, но по ходу развития проекта ограничения доставляют всё больше проблем.
Нарушения принципа подстановки — повод пересмотреть иерархию наследования или совсем от неё отказаться.
— SOLID принципы знаете?
Думаю, нет разработчика, который не слышал на собеседовании такой вопрос.
Расшифровку знают многие. А вот практические знания часто ограничиваются Single Responsibility и Interface Segregation.
Как применять остальные буквы в ежедневной работе — не всегда понятно. Больше всего вопросов возникает насчёт L — Liskov Substitution Principle. О нём и будет сегодняшний пост.
Полное определение звучит так:
Пусть q(x) является свойством верным относительно объектов x некоторого типа T. Тогда q(y) также должно быть верным для объектов y типа S, где S является подтипом типа T.
По-простому: если заменить класс А на подкласс B, то система будет работать корректно и без неожиданных сайд-эффектов.
A service1 = new A();Для наблюдателя service1 и service2 ведут себя совершенно одинаково. Класс-наследник дополняет поведение родителя, а не замещает его. В результате система работает более предсказуемо.
A service2 = new B();
Как это выглядит на практике:
1️⃣ Выходной тип метода в наследнике такой же как у родителя или расширенный
Базовый класс:
Info getInfo()
Наследник:✅ BigInfo getInfo()2️⃣ Подклассы не бросают дополнительных исключений, но могут уменьшить их список
❌ Object getInfo()
Базовый класс:
void save() throws FileNotFoundException
Наследник:✅ void save()Java — типизированный язык, поэтому пункты 1 и 2 контролируются компилятором.
❌ void save() throws FileNotFoundEx, InterruptedEx
3️⃣ Типы входных параметров те же или менее строгие. Пункт для общего понимания, тк для Java это неприменимо
Базовый класс:
void add(Account acc)
Наследник:✅ void add(Object acc)Следующие пункты компилятор уже не проверит, это целиком ответственность программиста.
❌ void add(AdminAccount acc)
4️⃣ Метод подкласса делает то же, что и метод базового класса
Базовый класс: метод
countVisitors
считает пользователейНаследник:
✅ Считает пользователей чуть по-другому
❌ Считает пользователей, обновляет статистику, сохраняет результат в БД
5️⃣ Метод наследника взаимодействует с теми же сущностями:
▪️ Метод родителя увеличивает счётчик - подкласс тоже увеличивает
▪️ Метод родителя не меняет поле - подкласс тоже не меняет
▪️ Метод родителя вызывает другие методы в определённом порядке - подкласс делает то же самое
А что можно вообще?
Если в подклассе объявлены новые поля, то методы подкласса могут делать с ними что угодно. На этом всё🙂
Правила выше - очень строгие. Но и наследование — штука непростая, это самая сильная связь между сущностями. Часто единственный плюс — это краткость кода, но по ходу развития проекта ограничения доставляют всё больше проблем.
Нарушения принципа подстановки — повод пересмотреть иерархию наследования или совсем от неё отказаться.
Паттерн проектирования, который в соответствии с принципом единственной обязанности передает другому объекту ответственность построения требуемых ему зависимостей внешнему, специально предназначенному для этого общему механизму - это:
Anonymous Poll
39%
Dependency Injection
9%
Dependency Invertion
39%
Inversion of Control
13%
Factory Method
Чем отличаются Dependency injection, Dependency invertion и Inversion of Control
Прошлый пост про Liskov, как говорится, "взорвал мой директ", поэтому на этой неделе расскажу про ещё два популярных принципа.
Сегодня про букву D из SOLID — Dependency Inversion. Что это, и чем отличается от Dependency injection и Inversion of Control. Понимание пригодится на собеседованиях, при чтении статей по дизайну и архитектуре.
Будем разбираться на простом примере: класс
1️⃣ Dependency injection
— компоненты создаются не внутри класса, а где-то в другом месте.
Как реализовать: перенести инициализацию логгера в конструктор или сеттер:
✅ Можно вынести всю конфигурацию в одно место. Или спихнуть часть забот фреймворку, например, Spring
⚔️Историческая справка
Когда Spring ещё не был популярен, в проектах использовался паттерн Service Locator.
Суть: компоненты создаются в классе ServiceLocator, а другие классы получают к ним доступ через статические методы:
▫️Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций
▫️Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракции
Как реализовать: использовать интерфейс логгера, а не конкретный класс
✅ Реализацию легко заменить
✅ Оба класса проще тестировать
Термин "абстракция" используется, потому что SOLID не привязан только к джаве. Группу методов можно выделить в интерфейс, в абстрактный класс и даже в обычный класс. Но интерфейс — наилучший вариант
3️⃣ IoC - Inversion of Control
В маленьких программах жизнь начинается в методе
Inversion of Control — это когда ход выполнения программы задаёт фреймворк. Например, Spring создаёт объекты, принимает запросы и не даёт программе завершиться.
Как реализовать: использовать аннотации фреймворка
✅ Низкая связность — код легко читать, менять и тестировать
Резюме:
🔸Dependency injection — класс не создаёт компоненты напрямую, они передаются через конструктор или сеттер
🔸Dependency invertion — класс работает с другими компонентами через интерфейс
🔸Inversion of Control — ход программы задаёт фреймворк
❗️Ответ на вопрос перед постом:
Это словоблудие относится к Dependency injection
Прошлый пост про Liskov, как говорится, "взорвал мой директ", поэтому на этой неделе расскажу про ещё два популярных принципа.
Сегодня про букву D из SOLID — Dependency Inversion. Что это, и чем отличается от Dependency injection и Inversion of Control. Понимание пригодится на собеседованиях, при чтении статей по дизайну и архитектуре.
Будем разбираться на простом примере: класс
Service
записывает логи в файл через класс FileLogger
:class FileLogger {…}Сделаем код чуть лучше с помощью разных принципов:
class Service {
FileLogger logger=new FileLogger();
}
1️⃣ Dependency injection
— компоненты создаются не внутри класса, а где-то в другом месте.
Как реализовать: перенести инициализацию логгера в конструктор или сеттер:
class Service {✅ Класс занимается только своей бизнес-логикой
FileLogger logger;
Service (FileLogger logger) {
this.logger=logger;
}
}
✅ Можно вынести всю конфигурацию в одно место. Или спихнуть часть забот фреймворку, например, Spring
⚔️Историческая справка
Когда Spring ещё не был популярен, в проектах использовался паттерн Service Locator.
Суть: компоненты создаются в классе ServiceLocator, а другие классы получают к ним доступ через статические методы:
class ServiceLocator {2️⃣ Dependency invertion (D из SOLID)
static Logger logger = …
static Logger getLogger() {
return logger;
}
}
class Service {
Logger logger=ServiceLocator.getLogger();
}
▫️Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций
▫️Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракции
Как реализовать: использовать интерфейс логгера, а не конкретный класс
interface Logger {…}✅ Интерфейс проще использовать, так как методов меньше
class FileLogger implements Logger {…}
class Service {
Logger logger=new FileLogger();
}
✅ Реализацию легко заменить
✅ Оба класса проще тестировать
Термин "абстракция" используется, потому что SOLID не привязан только к джаве. Группу методов можно выделить в интерфейс, в абстрактный класс и даже в обычный класс. Но интерфейс — наилучший вариант
3️⃣ IoC - Inversion of Control
В маленьких программах жизнь начинается в методе
main
. Программист создаёт объекты и вызывает их методы, все шаги явно прописаны.Inversion of Control — это когда ход выполнения программы задаёт фреймворк. Например, Spring создаёт объекты, принимает запросы и не даёт программе завершиться.
Как реализовать: использовать аннотации фреймворка
@Component class FileLogger {…}✅ Меньше скучного кода
@Component class Service {
@Autowired
FileLogger logger;
}
✅ Низкая связность — код легко читать, менять и тестировать
Резюме:
🔸Dependency injection — класс не создаёт компоненты напрямую, они передаются через конструктор или сеттер
🔸Dependency invertion — класс работает с другими компонентами через интерфейс
🔸Inversion of Control — ход программы задаёт фреймворк
❗️Ответ на вопрос перед постом:
Это словоблудие относится к Dependency injection
DRY для джуниора и сеньора
Раз уж пошли по базовым принципам, то сегодня разберём DRY: Don't Repeat Yourself.
Такой популярный и такой обманчиво простой.
Отношение к DRY эволюционирует по мере роста разработчика и проходит через четыре этапа:
🔸 Этап 1. Стажёр
Боготворит DRY, считает дублирование кода ужасным грехом. Действительно, зачем писать несколько раз одно и то же?
Для этого код максимально оптимизируется. Универсальные методы, универсальные статические методы, много входных параметров.
🔸 Этап 2. Джуниор
Любит DRY, но понимает, что любить — значит страдать.
Проект становится больше, а бизнес-логика — сложнее. Добавить в метод ещё один if уже не так просто. Сложно разбираться в коде, сложно писать тесты, но чего не сделаешь ради хорошего кода. А хороший код на этом этапе — это максимально сжатый код🙂
🔸 Этап 3. Мидл
Всё ещё любит DRY, но более возвышенно — на уровне иерархий и паттернов. Когда приходится дублировать код, то грустит, что на проекте плохая архитектура.
🔸 Этап 4. Сеньор
Распилил монолит на сервисы, реализовал десятки крупных фич и отдал сердце SOLID.
А теперь по делу
Часто начинающие разработчики считают, что хороший код — это суперконцентрированный код с кучей паттернов и хитрых приёмов.
В больших проектах хороший код — это тот, который легко читать, тестировать и поддерживать. Оптимизации и приёмчики - это совсем небольшая часть кодовой базы.
Если бизнес-процессы не пересекаются, то связывать их искусственно с помощью кода — плохая идея.
🙁 Класс User c 20 полями. 10 полей используются в 1 сервисе, другие 10 - в другом, половина объекта всегда пустая
🙁 Универсальный метод с 7 параметрами под разные случаи
🙁 Сложная иерархия с кучей шаблонных методов
Единственный плюс — меньше кода. Зато
❌ Плохая читаемость
❌ Сложно писать тесты
❌ Сильная связность. Функцию тяжело поменять или вынести в другой модуль
Я не говорю, что копипаст - единственный шанс на хорошую архитектуру. Переиспользовать код можно, если это действительно универсальные компоненты и та же цепочка бизнес-процессов. Но такое понимание приходит только с опытом.
Раз уж пошли по базовым принципам, то сегодня разберём DRY: Don't Repeat Yourself.
Такой популярный и такой обманчиво простой.
Отношение к DRY эволюционирует по мере роста разработчика и проходит через четыре этапа:
🔸 Этап 1. Стажёр
Боготворит DRY, считает дублирование кода ужасным грехом. Действительно, зачем писать несколько раз одно и то же?
Для этого код максимально оптимизируется. Универсальные методы, универсальные статические методы, много входных параметров.
🔸 Этап 2. Джуниор
Любит DRY, но понимает, что любить — значит страдать.
Проект становится больше, а бизнес-логика — сложнее. Добавить в метод ещё один if уже не так просто. Сложно разбираться в коде, сложно писать тесты, но чего не сделаешь ради хорошего кода. А хороший код на этом этапе — это максимально сжатый код🙂
🔸 Этап 3. Мидл
Всё ещё любит DRY, но более возвышенно — на уровне иерархий и паттернов. Когда приходится дублировать код, то грустит, что на проекте плохая архитектура.
🔸 Этап 4. Сеньор
Распилил монолит на сервисы, реализовал десятки крупных фич и отдал сердце SOLID.
А теперь по делу
Часто начинающие разработчики считают, что хороший код — это суперконцентрированный код с кучей паттернов и хитрых приёмов.
В больших проектах хороший код — это тот, который легко читать, тестировать и поддерживать. Оптимизации и приёмчики - это совсем небольшая часть кодовой базы.
Если бизнес-процессы не пересекаются, то связывать их искусственно с помощью кода — плохая идея.
🙁 Класс User c 20 полями. 10 полей используются в 1 сервисе, другие 10 - в другом, половина объекта всегда пустая
🙁 Универсальный метод с 7 параметрами под разные случаи
🙁 Сложная иерархия с кучей шаблонных методов
Единственный плюс — меньше кода. Зато
❌ Плохая читаемость
❌ Сложно писать тесты
❌ Сильная связность. Функцию тяжело поменять или вынести в другой модуль
Я не говорю, что копипаст - единственный шанс на хорошую архитектуру. Переиспользовать код можно, если это действительно универсальные компоненты и та же цепочка бизнес-процессов. Но такое понимание приходит только с опытом.
Что такое Serverless. Часть 1: предыстория
По статистике 2021 года около 10% проектов уже используют архитектуру Serverless.
Новые технологии — это классно, но важно понять, в чём вообще проблема и как она УЖЕ решается текущими средствами. И зачем решать её по-новому. Нужен чёткий ответ:
— Зачем переходить с микросервисов на Serverless? Что мы от этого получим?
Для ответа немного углубимся в историю инфраструктуры.
1️⃣ Всё своё
До 2006 года в каждой IT компании была особая комната — серверная. Системные администраторы настраивали сервера, следили за обновлениями, безопасностью, блоками питания и решали тысячу других вопросов.
❌ Много расходов на оборудование и персонал
❌ Железки надо закупать, подключать и нельзя сдать обратно. Сложно наращивать мощности или оптимизировать нагрузку
2️⃣ IasS — Infrastructure as a Service
В 2006 появился Amazon EC2 и начался тренд на IaaS: код запускается не на собственных серверах, а на арендованных.
Это самый простой вариант облачной инфраструктуры. Сейчас подобную услугу предлагают AWS, Google, DigitalOcean, Microsoft, IBM, SAP, для патриотов есть Яндекс и Сбер.
✅ Легко добавлять и сокращать ресурсы
✅ Меньше расходы на персонал
✅ Не надо беспокоиться об отключении электроэнергии и потопах
Инфраструктура всё ещё требует внимания — на арендованной виртуалке надо установить ОС, JVM, все службы и обновления, настроить компоненты и развернуть сервисы.
Большая часть этих операций почти у всех одинакова. Так что дальше расходимся на две ветки:
🅰️ PaaS — Platform as a Service
PaaS = IaaS + ОС + базовый мониторинг + легко подключаемые компоненты
Облачные провайдеры берут на себя больше рутинных операций. Выглядит как будто работаешь с удалённой машиной. Можно довольно легко добавить БД, мониторинг, кэши, очереди и связать их между собой в настройках.
Примеры PaaS: Heroku, AWS Elastic Beanstalk и тд
✅ Много готовых компонентов — можно быстро настроить работающую систему
❌ Набор компонентов большой, но всё же ограниченный. Если использовать что-то непопулярное, то придётся искать обходные пути
❌ Сильная привязка к вендору. Выбрал PaaS от амазона — скорее всего файловое хранилище, очереди и БД тоже будут амазон.
🅱️ Докер
Популярная альтернатива PaaS — докер-контейнеры в предоставленных виртуалках. В каждом контейнере свой runtime и все нужные для сервиса файлы.
Для управления контейнерами есть куча инструментов — Kubernetes, Mesos, Google Container Engine. Amazon ECR и Google CR помогают с хранением докер-образов, AWS Fargate — с масштабированием.
✅ Супер гибкость. Можно собрать любые компоненты с любыми настройками и связать их как угодно
❌ Cложно выбирать и долго настраивать
Заметили, что не было ни единого слова про архитектуру? PaaS и Docker только упрощают сборку инфраструктуры. Пока что нет разницы, что запускать внутри — гигантский монолит или сервис из трёх файлов.
В следующем посте перейдём уже к Serverless. Поговорим, почему это называется архитектурой и что ещё готовы взять на себя облачные провайдеры.
По статистике 2021 года около 10% проектов уже используют архитектуру Serverless.
Новые технологии — это классно, но важно понять, в чём вообще проблема и как она УЖЕ решается текущими средствами. И зачем решать её по-новому. Нужен чёткий ответ:
— Зачем переходить с микросервисов на Serverless? Что мы от этого получим?
Для ответа немного углубимся в историю инфраструктуры.
1️⃣ Всё своё
До 2006 года в каждой IT компании была особая комната — серверная. Системные администраторы настраивали сервера, следили за обновлениями, безопасностью, блоками питания и решали тысячу других вопросов.
❌ Много расходов на оборудование и персонал
❌ Железки надо закупать, подключать и нельзя сдать обратно. Сложно наращивать мощности или оптимизировать нагрузку
2️⃣ IasS — Infrastructure as a Service
В 2006 появился Amazon EC2 и начался тренд на IaaS: код запускается не на собственных серверах, а на арендованных.
Это самый простой вариант облачной инфраструктуры. Сейчас подобную услугу предлагают AWS, Google, DigitalOcean, Microsoft, IBM, SAP, для патриотов есть Яндекс и Сбер.
✅ Легко добавлять и сокращать ресурсы
✅ Меньше расходы на персонал
✅ Не надо беспокоиться об отключении электроэнергии и потопах
Инфраструктура всё ещё требует внимания — на арендованной виртуалке надо установить ОС, JVM, все службы и обновления, настроить компоненты и развернуть сервисы.
Большая часть этих операций почти у всех одинакова. Так что дальше расходимся на две ветки:
🅰️ PaaS — Platform as a Service
PaaS = IaaS + ОС + базовый мониторинг + легко подключаемые компоненты
Облачные провайдеры берут на себя больше рутинных операций. Выглядит как будто работаешь с удалённой машиной. Можно довольно легко добавить БД, мониторинг, кэши, очереди и связать их между собой в настройках.
Примеры PaaS: Heroku, AWS Elastic Beanstalk и тд
✅ Много готовых компонентов — можно быстро настроить работающую систему
❌ Набор компонентов большой, но всё же ограниченный. Если использовать что-то непопулярное, то придётся искать обходные пути
❌ Сильная привязка к вендору. Выбрал PaaS от амазона — скорее всего файловое хранилище, очереди и БД тоже будут амазон.
🅱️ Докер
Популярная альтернатива PaaS — докер-контейнеры в предоставленных виртуалках. В каждом контейнере свой runtime и все нужные для сервиса файлы.
Для управления контейнерами есть куча инструментов — Kubernetes, Mesos, Google Container Engine. Amazon ECR и Google CR помогают с хранением докер-образов, AWS Fargate — с масштабированием.
✅ Супер гибкость. Можно собрать любые компоненты с любыми настройками и связать их как угодно
❌ Cложно выбирать и долго настраивать
Заметили, что не было ни единого слова про архитектуру? PaaS и Docker только упрощают сборку инфраструктуры. Пока что нет разницы, что запускать внутри — гигантский монолит или сервис из трёх файлов.
В следующем посте перейдём уже к Serverless. Поговорим, почему это называется архитектурой и что ещё готовы взять на себя облачные провайдеры.
Что такое Serverless, часть 2
В прошлом посте рассмотрели, как инфраструктура понемногу переходила на аутсорс. Serverless — следующий этап такого перехода.
AWS Lambda — самая первая и популярная платформа для Serverless, поэтому дальше буду говорить про неё.
Важно! Есть ещё термин Lambda Architecture — это вообще про другое.
Итак, в чём суть.
Любое приложение — это набор функций. Допустим, в интернет-магазине три функции:
▫️ Добавить товары
▫️ Сделать заказ
▫️ Вывести список текущих заказов
Можно запихать всё в один артефакт — получится монолит. Можно каждую функцию обернуть в отдельный сервис — получатся микросервисы.
Serverless абстрагируется от этой проблемы. Мы не распределяем функциональность по артефактам и работаем просто с функциями. Проектируем не сборник фич, а отдельные маленькие компоненты.
Поэтому Serverless считается архитектурой. Или отдельной моделью проектирования, если термин "архитектура" кажется вам неподходящим.
Платформы, на которых запускаются Serverless приложения, называют FaaS — Functions as a Service.
В Петербурге Serverless используют около 10 компаний, что не очень много. Но тренд растёт, даже Сбер уже сделал свою FaaS платформу.
Как это работает?
Каждая функция состоит из:
▫️ Исполняемого кода
▫️ Списка зависимостей
▫️ Списка событий-триггеров
▫️ Конфигурации — количество памяти, необходимые права, время жизни функции и тд
Когда происходит событие из списка тригеров, FaaS платформа создаёт инстанс функции и обрабатывает его. Событием может быть HTTP запрос, сообщение из очереди, действие по расписанию. После обработки функция завершается или ждёт следующее событие в течение заданного времени.
Станет ли код проще?
Точно нет. Проектировать систему с изолированными функциями гораздо сложнее, чем слепить всё в монолит или десяток сервисов🙂
У Serverless свой деплой, тестирование и кодовая база. Даже если перенести только некоторые части приложения, общая схема заметно усложнится.
Зачем переходить на Serverless?
Если микросервисы в PaaS или докере нормально справляются, то должна быть веская причина что-то менять. Таких причин может быть две:
1️⃣ Функции развёртываются по необходимости. Облака это удобно, но иногда очень дорого. FaaS модель неплохо снижает стоимость при переменной нагрузке.
2️⃣ Масштабирование по умолчанию. В AWS Lambda автоматически масштабируются и сервисы, и остальные компоненты, например, БД. В докере и PaaS для этого нужно прилагать немало усилий.
Сколько стоит?
Допустим бэкенд мобильного приложения принимает за месяц 3 миллиона запросов, среднее время — 120мс. Выделим для одной функции 1536 МБ и процессор х86.
Плата за месяц в таком случае будет 2,7 доллара.
Это только AWS Lambda, остальные компоненты оплачиваются отдельно.
Что ещё
✅ Никаких Ansible, Docker, bash скриптов
✅ Базовый мониторинг и аналитика из коробки
✅ Spring Cloud и плагин IDEA AWS Toolkit сильно облегчают разработку
😐 Нужны отличные навыки проектирования
😐 Своеобразное тестирование
❌ Сложно адаптировать уже существующие приложения
❌ Больше задержек, response time может увеличиться
В целом Serverless — очевидный тренд, который набирает обороты. Жду в ближайшие два года тонну докладов на конференциях на эту тему, как когда-то было про микросервисы и реактивное программирование🙂
В прошлом посте рассмотрели, как инфраструктура понемногу переходила на аутсорс. Serverless — следующий этап такого перехода.
AWS Lambda — самая первая и популярная платформа для Serverless, поэтому дальше буду говорить про неё.
Важно! Есть ещё термин Lambda Architecture — это вообще про другое.
Итак, в чём суть.
Любое приложение — это набор функций. Допустим, в интернет-магазине три функции:
▫️ Добавить товары
▫️ Сделать заказ
▫️ Вывести список текущих заказов
Можно запихать всё в один артефакт — получится монолит. Можно каждую функцию обернуть в отдельный сервис — получатся микросервисы.
Serverless абстрагируется от этой проблемы. Мы не распределяем функциональность по артефактам и работаем просто с функциями. Проектируем не сборник фич, а отдельные маленькие компоненты.
Поэтому Serverless считается архитектурой. Или отдельной моделью проектирования, если термин "архитектура" кажется вам неподходящим.
Платформы, на которых запускаются Serverless приложения, называют FaaS — Functions as a Service.
В Петербурге Serverless используют около 10 компаний, что не очень много. Но тренд растёт, даже Сбер уже сделал свою FaaS платформу.
Как это работает?
Каждая функция состоит из:
▫️ Исполняемого кода
▫️ Списка зависимостей
▫️ Списка событий-триггеров
▫️ Конфигурации — количество памяти, необходимые права, время жизни функции и тд
Когда происходит событие из списка тригеров, FaaS платформа создаёт инстанс функции и обрабатывает его. Событием может быть HTTP запрос, сообщение из очереди, действие по расписанию. После обработки функция завершается или ждёт следующее событие в течение заданного времени.
Станет ли код проще?
Точно нет. Проектировать систему с изолированными функциями гораздо сложнее, чем слепить всё в монолит или десяток сервисов🙂
У Serverless свой деплой, тестирование и кодовая база. Даже если перенести только некоторые части приложения, общая схема заметно усложнится.
Зачем переходить на Serverless?
Если микросервисы в PaaS или докере нормально справляются, то должна быть веская причина что-то менять. Таких причин может быть две:
1️⃣ Функции развёртываются по необходимости. Облака это удобно, но иногда очень дорого. FaaS модель неплохо снижает стоимость при переменной нагрузке.
2️⃣ Масштабирование по умолчанию. В AWS Lambda автоматически масштабируются и сервисы, и остальные компоненты, например, БД. В докере и PaaS для этого нужно прилагать немало усилий.
Сколько стоит?
Допустим бэкенд мобильного приложения принимает за месяц 3 миллиона запросов, среднее время — 120мс. Выделим для одной функции 1536 МБ и процессор х86.
Плата за месяц в таком случае будет 2,7 доллара.
Это только AWS Lambda, остальные компоненты оплачиваются отдельно.
Что ещё
✅ Никаких Ansible, Docker, bash скриптов
✅ Базовый мониторинг и аналитика из коробки
✅ Spring Cloud и плагин IDEA AWS Toolkit сильно облегчают разработку
😐 Нужны отличные навыки проектирования
😐 Своеобразное тестирование
❌ Сложно адаптировать уже существующие приложения
❌ Больше задержек, response time может увеличиться
В целом Serverless — очевидный тренд, который набирает обороты. Жду в ближайшие два года тонну докладов на конференциях на эту тему, как когда-то было про микросервисы и реактивное программирование🙂
Enum и метод values
Если вы новичок и мало знаете про enum, то лучше начать с этого лонгрида. Сегодня обсудим итерацию через метод values и как его оптимизировать.
Итак, enums — это синтаксический сахар, который при компиляции превращается в обычный класс. Класс из примера выше превратится в
Если цикл с values используется в высоконагруженном коде, то разумно сохранить массив в отдельную переменную и переиспользовать её:
Пример из жизни
В Spring Web 5.2 в классе HttpStatus есть такой код:
Теперь код выглядит так:
Будет создано 3 массива: один внутри класса Number и два клона при вызове values()
Если вы новичок и мало знаете про enum, то лучше начать с этого лонгрида. Сегодня обсудим итерацию через метод values и как его оптимизировать.
Итак, enums — это синтаксический сахар, который при компиляции превращается в обычный класс. Класс из примера выше превратится в
public final class Number extends Enum<Number
>
Элементы енума станут статическими полями:public static final Number ONE;Внутри нового класса появится массив:
public static final Number TWO;
public static final Number THREE;
Number[] VALUES = { ONE, TWO, THREE};И его копия будет возвращаться в методе values:
return VALUES.clone();При каждом вызове values возвращается новая копия массива. Дело в том, что массивы — это изменяемый объект. Если возвращать ссылку на VALUES напрямую, любой желающий сможет поменять исходный массив:
Number.values()[2] = ONE;Это небезопасно, поэтому каждый раз возвращается копия.
Если цикл с values используется в высоконагруженном коде, то разумно сохранить массив в отдельную переменную и переиспользовать её:
static Number[] numbers = Number.values();Если код вызывается редко, то смысла в отдельной переменной нет.
for (Number n : numbers) {…}
Пример из жизни
В Spring Web 5.2 в классе HttpStatus есть такой код:
for (HttpStatus status : values()) {Этот цикл вызывается почти в каждом запросе, но только в этом году завели баг. К описанию прилагался бенчмарк: при нагрузке 600 запросов/сек код производил мегабайт мусора каждую секунду.
if (status.value == statusCode) {
return status;
}
}
Теперь код выглядит так:
private static final HttpStatus[] VALUES;Ответ на вопрос перед постом
static {
VALUES = values();
}
for (HttpStatus status : VALUES) {
if (status.value == statusCode) {
return status;
}
}
Будет создано 3 массива: один внутри класса Number и два клона при вызове values()
Потихоньку появляются доклады с Joker 2021. Конференции - это классно, но часто бывает, что:
▪️ Название доклада не отражает содержание
▪️ Тема интересная, но слушать тяжело
▪️ Доклад слишком простой или слишком сложный
Поделюсь с вами summary двух самых популярных докладов с последнего джокера.
Выделила основные идеи, полезные детали и сгруппировала всё, что получилось. Если тема заинтересует — посмотрите целиком. Если нет — сэкономите 2 часа жизни🙂
P.S. Все вопросы по содержанию не ко мне, а к спикерам
▪️ Название доклада не отражает содержание
▪️ Тема интересная, но слушать тяжело
▪️ Доклад слишком простой или слишком сложный
Поделюсь с вами summary двух самых популярных докладов с последнего джокера.
Выделила основные идеи, полезные детали и сгруппировала всё, что получилось. Если тема заинтересует — посмотрите целиком. Если нет — сэкономите 2 часа жизни🙂
P.S. Все вопросы по содержанию не ко мне, а к спикерам
Два доклада с Joker 2021
1️⃣ Алексей Нестеров, Олег Докука — Что нового в Spring Framework 6 (1:10:58)
Оба спикера никак не связаны со спрингом, просто обсуждают слухи о нововведениях.
Spring 6 использует java 17 и не будет работать на версиях ниже.
Дата выхода - октябрь 2022.
Кодовая база Spring 6 использует модули (фича java 9). Можно скомпилировать только нужные модули, и итоговый проект займёт меньше места.
Spring Native переедет в Spring Boot 3.
Spring будет связывать некоторые бины на этапе сборки, а не в рантайме. Это ускорит запуск приложений и потребление памяти.
Поменяются имена некоторых модулей. Например, пакет javax.* станет jakarta.*
В Spring WebFlux добавится поддержка HTTPClient из JDK 11.
Что удалится: Hessian, Http Invoker, JMS Invoker, JAX-WS, SecurityManager.
Что удалится, но менее вероятно: поддержка Joda Time, RxJava 1/2, FactoryBean, Autowired через сеттеры, конфигурация через XML.
Зачем смотреть: незачем. Информация недостоверная, много догадок, мало деталей.
2️⃣ Иван Крылов — От 11 к 17 версии Java (55:07)
Это доклад НЕ про новые фичи языка. Про sealed классы, records и switch спикер отсылает на доклад Тагира Валеева Java 17 для тех, кто в танке.
Два подхода к обновлению версии java в проекте:
1) Каждые полгода переходить на новую версию
Вариант подойдёт для проектов, где релизы происходят часто — каждый месяц или два. Или жизненный цикл проекта — это основной релиз и поддержка нескольких следующих релизов.
Плюс — всегда новая и поддерживаемая версия java
Минус — раз в полгода тратить несколько спринтов на переход
2) Переходить только на LTS версию
Встречается в проектах с редкими релизами — реже, чем раз в полгода.
Минус — переход всегда долгий и трудный, оттягивается до последнего.
Переезд на java 17 — это необходимость. Дело не в новых фичах, а в том, что поддержка старых версий ограничена и периодически прекращается. Миграция с 8 до 17 сложная, с 11 до 17 попроще.
С чем могут быть проблемы при переходе
▫️ Удалены финалайзеры из FileOutputStream, FileInputStream, zip.Deflater, zip.Inflater, zip.ZipFile, color.ICC_Profile. Это связано с предстоящим удалением финалайзеров в java 18.
▫️ Удалён конструктор URLDecoder.
▫️ Многие классы в AWT и Swing поменяли видимость.
▫️ Удалён Applet API.
Изменения виртуальной машины
▪️ GraalVM больше не связан с OpenJDK. Причина — административные проблемы и сложности с версионированием относительно OpenJDK.
▪️ Удалена оптимизация biased locking из-за плохой совместимости с Project Loom.
▪️ Добавлены CDS архивы для уменьшения времени старта VM.
▪️ Гибкий metaspace.
Изменения в сборщиках мусора
▫️ Удалён CMS.
▫️ ZGC готов к использованию в продакшене с 15 версии, в ранних версиях очень много багов.
▫️ G1 и Shenandoah возвращают неиспользуемую память ОС. Это уменьшает потребляемую память сервиса, и сервисы в облачной среде обойдутся дешевле.
Изменения в JDK
▪️ Классы по работе с TCP и UDP работают быстрее — DatagramSocketAPI, SocketAPI.
▪️ NullPointerException показывают больше информации.
▪️ Новый генератор случайных чисел.
Изменения Security
▫️ Добавлены Root сертификаты с разным сроком действия
▫️ Удалён SecurityManager
▫️ Удалены модули java.security, java.rmi.activation
Платформы
▪️ Добавилась поддержка Linux Alpine. ОС полезна для маленьких контейнеров в облаках. В ОС меньше команд, поэтому она более безопасна. Если маленькие контейнеры не нужны, то лучше выбрать другую ОС.
▪️ Полная поддержка ARM: унифицированный Linux порт, Windows, macOS
Какие проекты не успели войти в java 17
▫️ Loom: легковесные потоки в JVM
▫️ Panama: работа с нативным кодом, альтернатива JNI
▫️ Valhalla: новые типы данных value types — функциональные как классы и компактные как примитивы
Зачем смотреть: незачем. Большую часть видео перечисляются неязыковые фичи между 11 и 17 версией. О них говорят редко, так что для кого-то информация окажется новой. Но с тем же успехом можно посмотреть полный список фич и поискать слова, которые актуальны для вашего проекта или интересны лично вам.
1️⃣ Алексей Нестеров, Олег Докука — Что нового в Spring Framework 6 (1:10:58)
Оба спикера никак не связаны со спрингом, просто обсуждают слухи о нововведениях.
Spring 6 использует java 17 и не будет работать на версиях ниже.
Дата выхода - октябрь 2022.
Кодовая база Spring 6 использует модули (фича java 9). Можно скомпилировать только нужные модули, и итоговый проект займёт меньше места.
Spring Native переедет в Spring Boot 3.
Spring будет связывать некоторые бины на этапе сборки, а не в рантайме. Это ускорит запуск приложений и потребление памяти.
Поменяются имена некоторых модулей. Например, пакет javax.* станет jakarta.*
В Spring WebFlux добавится поддержка HTTPClient из JDK 11.
Что удалится: Hessian, Http Invoker, JMS Invoker, JAX-WS, SecurityManager.
Что удалится, но менее вероятно: поддержка Joda Time, RxJava 1/2, FactoryBean, Autowired через сеттеры, конфигурация через XML.
Зачем смотреть: незачем. Информация недостоверная, много догадок, мало деталей.
2️⃣ Иван Крылов — От 11 к 17 версии Java (55:07)
Это доклад НЕ про новые фичи языка. Про sealed классы, records и switch спикер отсылает на доклад Тагира Валеева Java 17 для тех, кто в танке.
Два подхода к обновлению версии java в проекте:
1) Каждые полгода переходить на новую версию
Вариант подойдёт для проектов, где релизы происходят часто — каждый месяц или два. Или жизненный цикл проекта — это основной релиз и поддержка нескольких следующих релизов.
Плюс — всегда новая и поддерживаемая версия java
Минус — раз в полгода тратить несколько спринтов на переход
2) Переходить только на LTS версию
Встречается в проектах с редкими релизами — реже, чем раз в полгода.
Минус — переход всегда долгий и трудный, оттягивается до последнего.
Переезд на java 17 — это необходимость. Дело не в новых фичах, а в том, что поддержка старых версий ограничена и периодически прекращается. Миграция с 8 до 17 сложная, с 11 до 17 попроще.
С чем могут быть проблемы при переходе
▫️ Удалены финалайзеры из FileOutputStream, FileInputStream, zip.Deflater, zip.Inflater, zip.ZipFile, color.ICC_Profile. Это связано с предстоящим удалением финалайзеров в java 18.
▫️ Удалён конструктор URLDecoder.
▫️ Многие классы в AWT и Swing поменяли видимость.
▫️ Удалён Applet API.
Изменения виртуальной машины
▪️ GraalVM больше не связан с OpenJDK. Причина — административные проблемы и сложности с версионированием относительно OpenJDK.
▪️ Удалена оптимизация biased locking из-за плохой совместимости с Project Loom.
▪️ Добавлены CDS архивы для уменьшения времени старта VM.
▪️ Гибкий metaspace.
Изменения в сборщиках мусора
▫️ Удалён CMS.
▫️ ZGC готов к использованию в продакшене с 15 версии, в ранних версиях очень много багов.
▫️ G1 и Shenandoah возвращают неиспользуемую память ОС. Это уменьшает потребляемую память сервиса, и сервисы в облачной среде обойдутся дешевле.
Изменения в JDK
▪️ Классы по работе с TCP и UDP работают быстрее — DatagramSocketAPI, SocketAPI.
▪️ NullPointerException показывают больше информации.
▪️ Новый генератор случайных чисел.
Изменения Security
▫️ Добавлены Root сертификаты с разным сроком действия
▫️ Удалён SecurityManager
▫️ Удалены модули java.security, java.rmi.activation
Платформы
▪️ Добавилась поддержка Linux Alpine. ОС полезна для маленьких контейнеров в облаках. В ОС меньше команд, поэтому она более безопасна. Если маленькие контейнеры не нужны, то лучше выбрать другую ОС.
▪️ Полная поддержка ARM: унифицированный Linux порт, Windows, macOS
Какие проекты не успели войти в java 17
▫️ Loom: легковесные потоки в JVM
▫️ Panama: работа с нативным кодом, альтернатива JNI
▫️ Valhalla: новые типы данных value types — функциональные как классы и компактные как примитивы
Зачем смотреть: незачем. Большую часть видео перечисляются неязыковые фичи между 11 и 17 версией. О них говорят редко, так что для кого-то информация окажется новой. Но с тем же успехом можно посмотреть полный список фич и поискать слова, которые актуальны для вашего проекта или интересны лично вам.
State of spring 2021
Поделюсь статистикой State of spring 2021 от VMWare. Данные релевантны для энтерпрайзных проектов из Европы и США.
Сама статистика довольно скучная, поэтому дам небольшое овервью по некоторым модулям.
🔸 79% используют Spring Security в рабочих проектах
🔸 79% Spring Data
Простые интерфейсы для работы с данными из разных БД. Обычно используется в связке с:
▫️ 78% JPA
▫️ 74% JDBC
▫️ 46% MongoDB
▫️ 37% Redis
▫️ 31% ElasticSearch
🔸 73% WebMVC
🔸 61% Boot
🔸 39% Kafka
🔸 38% Batch — фоновая обработка большого количества данных
🔸 37% Cloud
Не смотрите на название, модуль используется не только в облачной инфраструктуре.
Берёт на себя типовые задачи в микросервисной архитектуре:
▫️ Service Discovery — получить адрес другого сервиса
▫️ Добавить в логи информацию про конкретный сервис
🔸 35% WebFlux — поддержка реактивных библиотек
🔸 32% Integration
Ещё один уровень абстракции и набор готовых компонентов, которые соединяются через Enterprise Integration Patterns. Через конфигурацию можно описать несложную логику вроде "прочитай XML, преврати в JSON, отправь по HTTP".
🔸 3% Native
Компиляция и запуск на GraalVM. Уменьшает время старта и потребление памяти, хорошо работает с контейнерами.
Большинство опрошенных в восторге от Native, но пока не торопятся внедрять в рабочий проект. Потому что Native пока в стадии бета и GraalVM слишком незрелая технология. Но 65% планируют использовать Native в будущем.
Поделюсь статистикой State of spring 2021 от VMWare. Данные релевантны для энтерпрайзных проектов из Европы и США.
Сама статистика довольно скучная, поэтому дам небольшое овервью по некоторым модулям.
🔸 79% используют Spring Security в рабочих проектах
🔸 79% Spring Data
Простые интерфейсы для работы с данными из разных БД. Обычно используется в связке с:
▫️ 78% JPA
▫️ 74% JDBC
▫️ 46% MongoDB
▫️ 37% Redis
▫️ 31% ElasticSearch
🔸 73% WebMVC
🔸 61% Boot
🔸 39% Kafka
🔸 38% Batch — фоновая обработка большого количества данных
🔸 37% Cloud
Не смотрите на название, модуль используется не только в облачной инфраструктуре.
Берёт на себя типовые задачи в микросервисной архитектуре:
▫️ Service Discovery — получить адрес другого сервиса
▫️ Добавить в логи информацию про конкретный сервис
🔸 35% WebFlux — поддержка реактивных библиотек
🔸 32% Integration
Ещё один уровень абстракции и набор готовых компонентов, которые соединяются через Enterprise Integration Patterns. Через конфигурацию можно описать несложную логику вроде "прочитай XML, преврати в JSON, отправь по HTTP".
🔸 3% Native
Компиляция и запуск на GraalVM. Уменьшает время старта и потребление памяти, хорошо работает с контейнерами.
Большинство опрошенных в восторге от Native, но пока не торопятся внедрять в рабочий проект. Потому что Native пока в стадии бета и GraalVM слишком незрелая технология. Но 65% планируют использовать Native в будущем.
Ну что, какие планы на эту неделю?
Anonymous Poll
43%
Работать как обычно 🧑🏻💻
19%
Закрывать простые задачки
5%
Шутить в рабочих чатах
33%
Отдыхать и не думать о работе🎄
Мой вариант — последний😊 Чищу мандаринки, смотрю статистику канала и радуюсь.
Спасибо, что читаете нудные посты без картинок, помогаете найти ошибки и задаёте интересные вопросы. Благодаря вам канал живёт и развивается!
Продолжим этот движ в 2022🚀
Спасибо, что читаете нудные посты без картинок, помогаете найти ошибки и задаёте интересные вопросы. Благодаря вам канал живёт и развивается!
Продолжим этот движ в 2022🚀