Java: fill the gaps
12.6K subscribers
7 photos
216 links
Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк

🔥Тот самый курс по многопочке🔥
https://fillthegaps.ru/mt

Комплименты, вопросы, предложения: @utki_letyat
Download Telegram
Value types: концепт идентичности

Сегодня продолжу разбирать JEP 401: value types. Начало в прошлом посте.

В JEP слово identity встречается 87 раз, практически в каждом абзаце. В этом посте разберу концепт идентичности и что меня в нём смущает.

В систему типов вводится новый термин - идентичность (identity):
▫️ У примитивов идентичности нет. 10 в одном месте ничем не отличается от 10 в другом месте
▫️ У ссылочных типов идентичность есть, каждый объект уникален. 2 объекта UserDTO - это разные объекты, даже если поля одинаковые
▫️ У value классов идентичности нет

С первого взгляда кажется, что идентичность это про == и equals. Но по мере чтения JEP растёт ощущение, что что-то не так. Например

1️⃣ Нет чёткого определения идентичности

Понятия типа identity относятся больше к проектированию модели данных. Value object, агрегаты и прочий DDD.

Java — это всё ещё язык программирования уровня циклы/классы. Примитивы и ссылочные типы описываются техническими характеристиками - стек, куча, наличие методов и тд.

Идейно Identity — что-то про возможность сравнения объектов между собой. Но что это означает с технической точки зрения в java — непонятно.

2️⃣ Сравнение value типов описано в JEP очень противоречиво:

💁🏼 С одной стороны, много раз повторили, что == сравнивает value объекты по значению полей. Как в примитивах, 123 == 123
🙋🏼 Но для некоторых value объектов equals вернет true, а == вернет false. Поэтому жирным шрифтом рекомендуют использовать equals

value класс - это всё ещё класс. Маловероятно, что кто-то будет использовать == для сравнения объектов. И объяснение какое-то есть. Но осадочек, что тема мутная, всё равно остаётся.

🌚 Конспирологическая теория, зачем на самом деле нужна identity

Внятного определения идентичности нет, значит понятие само по себе не важно. Гораздо важнее, что такое "тип без индентичности". Здесь написано чётко. Тип без идентичности
▫️ Immutable: неизменяемый
▫️ Interchangeable: взаимозаменяемый. Необязательно различать две сущности, если у них одинаковые значения

Для сущностей с этими свойствами добавляется пачка оптимизаций. Помимо уплощения кучи в списках есть, например, такая:
При создании value объекта через new, JVM может не создавать новый объект, а найти уже такой же существующий. == для таких объектов будет возвращать true.

Подобная идея лежит в основе пулов для базовых типов. При вызове Integer.valueOf(127) не создаётся новый объект, а берётся уже существующий из пула. Для классов-обёрток это существенная экономия.

Текущая цель модификатора value и концепта identity (вернее его отсутствия) - обозначить, к каким объектам можно применить пачку оптимизаций.

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

Годы работы и преподавания не проходят бесследно. Если что-то может быть понятно неверно - оно будет понято неверно💯 Поэтому пока оцениваю новую фичу на троечку

И ответ на вопрос перед постом. Сейчас в консоли будет false, объекты сравниваются по ссылке. Возможно, после введения value types тот же код вернёт true, и популярный вопрос с собеседований получит вторую жизнь. Но может и нет💅
👍62🔥3121👎4
Лучшие посты и новости

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

Топ-5 постов этого года


▫️ База по Postgres: почему коннекшены это дорого, и что делает буфер и WAL
▫️ Почему однопоточный Redis быстрее многопоточного ConcurrentHashMap
▫️ CRaC — новая фича с большим потенциалом
▫️ Как прогреть кэши в Spring Boot
▫️ Как устроена многопоточность в разных языках

Мой фаворит - пост Почему в Set.of нельзя добавить дубликаты Маленькое расследование и теперь моя любимая байка про джаву

Новости для пользователей hard skills бота

1. Если ваша подписка попадает на январские праздники, вам добавляются две недели подписки. Хотите отдохнуть - отдыхайте, оплаченные дни никуда не пропадут.
(да, если подписаться сейчас, тоже будет 1.5 месяца по цене 1)
2. Появилась кнопка с итогами года, зацените!

Традиционное спасибо

Любимые подписчики, спасибо, что читаете. Вы все умнички! Что бы ни принёс следующий год, мы со всем справимся. Счастья нам всем в новом году💖
115🎄67🔥36
Чистая архитектура: главы 1-2

Начала читать книгу Clean architecture Роберта Мартина.

Давно чувствую в этой теме какой-то пробел. Идея о непорочной бизнес-логике мне нравится, но на практике я не видела хороших примеров. 

Видела сервисы, которые задумывались как "чистые", но в итоге становились проблемными. Протекающие слои, костыли, проблемы с транзакциями и атомарностью в целом. Не всегда понятно, как вписать некоторые задачи в рамку чистой архитектуры.

У меня нет больших ожиданий. Книга 2017 года, да и Мартин вряд ли плотно работал в энтерпрайзе, когда её писал. Но мне интересно почитать первоисточник и идейную составляющую.

И по первым впечатлениям - книга очень противоречивая.

Поднимается много тем - от парадигм программирования до структуры папок в проекте. Есть что обсудить👌

Но книгу читать сложно. Бесконечно много исторических справок и отступлений. Не всегда понятно, что хочет сказать автор, и как это связано с чистой архитектурой.

Поэтому решила делиться с вами процессом чтения. Это не будет подробный конспект, скорее спидран/реакция/саммари. Буду отмечать, что мне показалось интересным. Думаю, получится полезно.

Главы 1 и 2 - это введение. Большой смысловой нагрузки пока нет.

⭐️ Глава 1 ⭐️

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

⭐️ Глава 2 ⭐️

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

Собственно, и всё.

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

В целом, Мартин кажется противоречивым персонажем, поэтому интересно, что будет дальше. Идеалистические концепты? Конкретные инструкции? 400 страниц воды? Посмотрим, обсудим
🔥114👍4722🎄4👎3
Чистая архитектура. Главы 3-5

Продолжаем спидран по Clean Architecture Роберта Мартина.

⭐️ Глава 3. Парадигмы программирования ⭐️

определяют способы написания и организации кода. Их 3:

▫️ Структурная - ход программы описывается явно через условия и циклы. Используется для описания алгоритмов
▫️ Обьектно-ориентированная - программа описывается как взаимодействие объектов. Используется полиморфизм, ход работы не всегда очевиден
▫️ Функциональная - базируется на неизменяемости

⭐️ Глава 4. Структурное программирование ⭐️

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

⭐️ Глава 5. Обьектно-ориентированное (ОО) программирование ⭐️

Роберт рассуждает, что полезного и нового предлагает ООП

▫️ Инкапсуляция - не уникальна для ООП, плюс в современных языках не решает свою задачу.

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

В С (предшественник С++) определение функции и реализация разнесены по разным файлам, там инкапсуляция нормальная. В современных ОО языках инкапсуляция слабая. Нет разделения на определение и реализацию. Чтобы посмотреть, что умеет класс, надо зайти в класс, и тут открывается вся его внутрянка. Детали реализации как на ладони.

(мысль интересная, но с текущими IDE это не актуально. Выпадающие списки методов делают большой вклад в сокрытие сложности)

▫️ Наследование - всего лишь переопределение переменных/функций в ограниченном контексте. Добавочной ценности для архитектуры нет.

▫️ Полиморфизм легко и безопасно реализован в ОО языках, и в этом его главная ценность. Благодаря полиморфизму расцвел концепт инверсии зависимостей.

Дальше автор объясняет dependency inversion. Тут меня знатно побомбило.

DI - базовый кирпичик и первый шаг к чистой архитектуре. Это самая важная часть раздела. И она объяснена очень плохо:
▪️ Роберт вводит 2 типа стрелочек: source code dependency и flow of control. Не объясняя, что это такое
▪️ Рисует схему, где стрелочки идут вместе
▪️ Добавляет в схему ещё один квадрат (интерфейс) и поворачивает одну стрелку в другую сторону

На этом всё. Ни одного примера с кодом, только квадратики и стрелки. Этого недостаточно, чтобы каждый читатель понял, что нужно делать и в чём инверсия. Просто добавить интерфейс к каждому классу? Service и ServiceImpl это и есть тот самый power move?

Особенно дико такое объяснение выглядит на контрасте с менее важными темами. Рядом есть детальный разбор
✔️ разницы инкапсуляции в С и С++
✔️ как в С (язык без классов) можно имитировать наследование
✔️ как работают атомики в Clojure

Всё это разжевано гораздо подробнее, чем DI🤦

В 11 главе Роберт сделает второй заход в dependency inversion, но (спойлер) всё останется на уровне "просто добавь интерфейс". Это рекомендации по форме, но не по содержанию.

Интерфейсом может быть не только дополнительный квадратик, но и набор публичных методов класса. Чем больше этот набор скрывает детали реализации и упрощает работу с классом, тем лучше. И чтобы уменьшить связность, не обязательно множить сущности.
👍6425🔥17👎5
Тестовый контекст поднимается 2 минуты. У нас 4 класса с интеграционными тестами, их конфигурация ниже. Настройки прогона тестов стандартные. Сколько времени займет выполнение этих тестов?
@SpringBootTest
public class UserIT{...}

@ActiveProfiles("test")
@SpringBootTest
public class BidProcessingIT{...}

@SpringBootTest(properties = {
"user.rebalance.enabled=true"
})
public class RebalanceAccountIT{...}

@ActiveProfiles("test")
@SpringBootTest
public class AccountProcessingIT{...}
5
Как переиспользовать контекст в интеграционных тестах

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

В спринге интеграционным тестом называется тот, для которого нужен контекст. Контекст - это набор бинов, свойств, профилей и тд. Поднимать его для каждого теста долго, поэтому Spring по возможности переиспользует созданные ранее контексты.

Если в коде теста какой-то бин помечен @MockitoBean, @MockitoSpyBean или @TestBean, Spring создает для него прокси. Получается уникальный контекст, который нужно поднимать отдельно и нельзя в дальнейшем переиспользовать.

К новому контексту приводит переопределение свойств, указание профиля, конфигурация MockMvc и тд

Ответ на вопрос перед постом - прогон тестов будет занимать чуть больше 6 минут, потому что формируется 3 контекста. Для тестов BidProcessingIT и AccountProcessingIT используется один контекст.

🤔 Как оптимизировать выполнение тестов?

Популярный прием, который помогает зафиксировать контекст - базовый класс с тестовой конфигурацией. В базовом классе настраивается тестовый конфиг, тестконтейнеры, решается вопрос с секьюрити, наполнением и очисткой БД и тд
@SpringBootTest
@AutoConfigureMockMvc
@TestPropertySource(…)
@TestExecutionListeners(…)
@ActiveProfile("test")
// куча других аннотаций
public abstract class BaseIntegrationTest {
// тестконтейнеры
// заглушки
}

Остальные классы наследуются от базового:
class UserIT extends BaseIntegrationTest {
@Autowired
  private MockMvc mockMvc;
  @Test
  public void shouldCreateUser() {…}
}

При такой схеме важно следить, чтобы в наследниках не заменялись бины, свойства и тд. Иначе для них будет создан новый контекст.

На практике редко используется один базовый класс, чаще это иерархия под разные случаи. При грамотном проектировании и сознательности разработчиков интеграционные тесты выполняются за адекватное время даже для больших проектов👌
57👍35🔥21👎2