«Что такое CAP-теорема и почему она важна в распределённых системах?»
CAP-теорема утверждает, что в распределённой базе данных одновременно можно гарантировать только два свойства из трёх - Consistency, Availability и Partition Tolerance. Так как сетевые разделения неизбежны, приходится выбирать между консистентностью и доступностью.
Consistency означает, что каждое чтение возвращает самую последнюю запись, независимо от того, на какой реплике выполняется запрос. Для сильной консистентности обычно требуется координация между узлами перед тем, как подтвердить ответ.
Availability означает, что каждый запрос получает ответ, даже если это не самые свежие данные. Система остаётся доступной и отвечает на запросы, несмотря на сбои отдельных узлов.
Partition Tolerance означает, что система продолжает работать даже при сетевых разрывах или потере сообщений. В реальности разделения всегда происходят, поэтому это свойство нельзя исключить.
Так как P всегда должно выполняться, в реальных системах приходится выбирать между CP и AP.
CP-системы отдают приоритет консистентности и устойчивости к разделению, жертвуя доступностью во время сбоев. Примеры — ZooKeeper, Google Spanner, CockroachDB.
AP-системы делают ставку на доступность и устойчивость к разделению, во время проблем могут отдавать устаревшие или несовпадающие данные, но со временем данные приходят к единому состоянию. Примеры — Cassandra, Riak, DynamoDB.
CP имеет смысл для финансовых и критически важных приложений, где важна корректность данных. AP лучше подходит для масштабных систем, где приоритет — чтобы сервис всегда отвечал, даже ценой временной рассогласованности. Многие современные базы позволяют настраивать уровень консистентности под конкретные задачи.
👉 Java Portal
CAP-теорема утверждает, что в распределённой базе данных одновременно можно гарантировать только два свойства из трёх - Consistency, Availability и Partition Tolerance. Так как сетевые разделения неизбежны, приходится выбирать между консистентностью и доступностью.
Consistency означает, что каждое чтение возвращает самую последнюю запись, независимо от того, на какой реплике выполняется запрос. Для сильной консистентности обычно требуется координация между узлами перед тем, как подтвердить ответ.
Availability означает, что каждый запрос получает ответ, даже если это не самые свежие данные. Система остаётся доступной и отвечает на запросы, несмотря на сбои отдельных узлов.
Partition Tolerance означает, что система продолжает работать даже при сетевых разрывах или потере сообщений. В реальности разделения всегда происходят, поэтому это свойство нельзя исключить.
Так как P всегда должно выполняться, в реальных системах приходится выбирать между CP и AP.
CP-системы отдают приоритет консистентности и устойчивости к разделению, жертвуя доступностью во время сбоев. Примеры — ZooKeeper, Google Spanner, CockroachDB.
AP-системы делают ставку на доступность и устойчивость к разделению, во время проблем могут отдавать устаревшие или несовпадающие данные, но со временем данные приходят к единому состоянию. Примеры — Cassandra, Riak, DynamoDB.
CP имеет смысл для финансовых и критически важных приложений, где важна корректность данных. AP лучше подходит для масштабных систем, где приоритет — чтобы сервис всегда отвечал, даже ценой временной рассогласованности. Многие современные базы позволяют настраивать уровень консистентности под конкретные задачи.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤4
Почему устойчивые микросервисы продолжают работать даже когда их зависимости рушатся? У них есть общий приём Circuit Breaker и в Spring Boot это делается довольно просто.
В распределённых системах - отказы обычное дело, базы могут подвисать, API уходить в таймауты, сеть лагать. Без защиты такие сбои начинают цепной реакцией сжирать потоки и валить сервис. Circuit Breaker решает проблему когда количество ошибок превышает порог он размыкается и быстро возвращает фолбэк вместо того чтобы долбиться в нерабочий ресурс.
В Spring Boot для этого используют лёгкую и современную библиотеку Resilience4j. Там настраиваются порог ошибок, время ожидания и правила восстановления. Как только выключатель уходит в open все запросы режутся. По истечении тайм аута он переключается в half open и пропускает несколько тестовых вызовов. Если они проходят успешно, схема снова закрывается и трафик идёт как обычно. Если нет возвращается в open.
В итоге сервис остаётся отзывчивым даже если его зависимости лежат. В паре с таймаутами, ретраями и bulkhead изоляцией, это превращается в полноценный набор для отказоустойчивости. Польза очевидна, вместо падения всей системы - аккуратная деградация и стабильность, что и отличает продакшен уровень.
👉 Java Portal
В распределённых системах - отказы обычное дело, базы могут подвисать, API уходить в таймауты, сеть лагать. Без защиты такие сбои начинают цепной реакцией сжирать потоки и валить сервис. Circuit Breaker решает проблему когда количество ошибок превышает порог он размыкается и быстро возвращает фолбэк вместо того чтобы долбиться в нерабочий ресурс.
В Spring Boot для этого используют лёгкую и современную библиотеку Resilience4j. Там настраиваются порог ошибок, время ожидания и правила восстановления. Как только выключатель уходит в open все запросы режутся. По истечении тайм аута он переключается в half open и пропускает несколько тестовых вызовов. Если они проходят успешно, схема снова закрывается и трафик идёт как обычно. Если нет возвращается в open.
В итоге сервис остаётся отзывчивым даже если его зависимости лежат. В паре с таймаутами, ретраями и bulkhead изоляцией, это превращается в полноценный набор для отказоустойчивости. Польза очевидна, вместо падения всей системы - аккуратная деградация и стабильность, что и отличает продакшен уровень.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥4❤1
Почему логи уже не спасают в мире микросервисов
Когда один запрос может пролетать через 10+ сервисов, по логам не всегда поймёшь, где именно произошёл затык или фейл.
Тут на сцену выходит OpenTelemetry — современный cloud-native стандарт для распределённого трейсинга.
Он умеет работать с Jaeger, Tempo, Zipkin, Prometheus, Grafana и собирает не только трейсинг, но ещё и метрики с логами — всё в одном месте.
Логи отвечают на вопрос что произошло.
Трейсы показывают где это произошло.
А ты уже внедрял OpenTelemetry в свои сервисы?🍺
👉 Java Portal
Когда один запрос может пролетать через 10+ сервисов, по логам не всегда поймёшь, где именно произошёл затык или фейл.
Тут на сцену выходит OpenTelemetry — современный cloud-native стандарт для распределённого трейсинга.
Он умеет работать с Jaeger, Tempo, Zipkin, Prometheus, Grafana и собирает не только трейсинг, но ещё и метрики с логами — всё в одном месте.
Логи отвечают на вопрос что произошло.
Трейсы показывают где это произошло.
А ты уже внедрял OpenTelemetry в свои сервисы?
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍5
Задумывались ли вы, что поддерживает приложение Spring Boot в живом состоянии и работоспособном? ❔
Это
Объясняю проще:
В Spring Boot
Он отвечает за загрузку конфигураций, создание и связывание бинов, управление их жизненным циклом и предоставляет такие сервисы, как загрузка ресурсов, интернационализация, доступ к окружению/профилям и публикация событий приложения.
Когда контекст обновляется (например, при старте приложения), он читает источники конфигурации (аннотации, свойства, XML и автоконфигурацию), регистрирует
Обычно вы взаимодействуете с бинами через dependency injection, а не через их поиск, но вы можете получить бин программно с помощью
Spring Boot строится поверх Spring, используя
Существуют различные реализации
Вкратце,
👉 Java Portal
Это
ApplicationContext
, настоящее сердце Spring Boot.Объясняю проще:
В Spring Boot
ApplicationContext
это центральный контейнер времени выполнения, который управляет компонентами вашего приложения, называемыми beans (бинами).Он отвечает за загрузку конфигураций, создание и связывание бинов, управление их жизненным циклом и предоставляет такие сервисы, как загрузка ресурсов, интернационализация, доступ к окружению/профилям и публикация событий приложения.
Когда контекст обновляется (например, при старте приложения), он читает источники конфигурации (аннотации, свойства, XML и автоконфигурацию), регистрирует
BeanPostProcessors
и BeanFactoryPostProcessors
, и по умолчанию заранее создаёт синглтон-бины, чтобы они были готовы к использованию.Обычно вы взаимодействуете с бинами через dependency injection, а не через их поиск, но вы можете получить бин программно с помощью
getBean
Spring Boot строится поверх Spring, используя
ApplicationContext
для регистрации и конфигурации авто-конфигурируемых бинов, что позволяет использовать конвенции "из коробки".ConfigurableApplicationContext
предоставляет операции жизненного цикла, такие как refresh() и close(), а также публикует события жизненного цикла, такие как ContextRefreshedEvent
Существуют различные реализации
ApplicationContext
для разных случаев использования, например, AnnotationConfigApplicationContext для Java-конфигурации и WebApplicationContext для веб-приложений.Вкратце,
ApplicationContext
это сердце Spring Boot, потому что многое из поведения фреймворка, включая управление бинами, конфигурацию и точки интеграции, организовано и обслуживается через него. Он поддерживает иерархии контекстов для изоляции в модульных приложениях и обеспечивает надёжное разрешение ресурсов.Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤3
Выбор структуры данных
I. Быстрый поиск → HashMap
II. Отсортированный порядок → Сбалансированное BST (AVL, Красно-Чёрное дерево)
III. FIFO / LIFO → Очередь / Стек
IV. Поиск по префиксу → Trie
V. Связность → Union-Find
VI. Запросы по диапазону → Segment Tree
VII. Медиана потока → Две кучи (Two Heaps)
VIII. Скользящее окно → Deque
IX. Вытеснение кэша → LRU Cache
👉 Java Portal
I. Быстрый поиск → HashMap
II. Отсортированный порядок → Сбалансированное BST (AVL, Красно-Чёрное дерево)
III. FIFO / LIFO → Очередь / Стек
IV. Поиск по префиксу → Trie
V. Связность → Union-Find
VI. Запросы по диапазону → Segment Tree
VII. Медиана потока → Две кучи (Two Heaps)
VIII. Скользящее окно → Deque
IX. Вытеснение кэша → LRU Cache
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍4
Ты на интервью по системному дизайну. Спрашивают: «Как бы ты спроектировал слой кэширования для высоконагруженного веб-приложения?»
Вот подробный подход:
I. Что кэшировать → Кэшируй дорогие запросы к БД, горячие данные с большим количеством чтений и статические ресурсы; избегай очень динамичных, гигантских объектов или чувствительной PII, если только она не зашифрована и не защищена доступом.
II. Хранилище кэша → Используй Redis или Memcached для распределённого in-memory кэша, CDN для статики на краю сети, локальные in-process кэши (например, Caffeine) для сверхнизкой задержки L1.
III. Паттерн и запись → По умолчанию Cache-Aside: читаем из кэша, при промахе читаем из БД и заполняем кэш. Для сильной консистентности чтения — Write-Through (пишем одновременно в кэш и БД). Всегда сначала коммитим БД, потом обновляем/инвалидируем кэш, чтобы не дать устаревшим данным просочиться.
IV. Истечение и вытеснение → TTL для каждого типа данных, чтобы держать кэш свежим, политики вытеснения (LRU/LFU) для управления памятью, negative caching для промахов, чтобы не перегружать БД. Ограничивай размер объектов, чтобы не создавать проблемы с памятью и сборкой мусора.
V. Масштабирование и «горячие ключи» → Масштабируй через consistent hashing и реплики для пропускной способности чтения. Многоуровневый кэш (edge→regional→local). Горячие ключи — локальные L1 кэши, отдельные кэши для горячих ключей, либо rate-limiting запросов.
VI. Надёжность и наблюдаемость → Предотвращай «каскады» промахов через request coalescing, короткие блокировки (SETNX + expiry) или serve-stale-while-revalidate с jittered TTL. Мониторь hit rate, latency, eviction, память и replication lag с алертами.
👉 Java Portal
Вот подробный подход:
I. Что кэшировать → Кэшируй дорогие запросы к БД, горячие данные с большим количеством чтений и статические ресурсы; избегай очень динамичных, гигантских объектов или чувствительной PII, если только она не зашифрована и не защищена доступом.
II. Хранилище кэша → Используй Redis или Memcached для распределённого in-memory кэша, CDN для статики на краю сети, локальные in-process кэши (например, Caffeine) для сверхнизкой задержки L1.
III. Паттерн и запись → По умолчанию Cache-Aside: читаем из кэша, при промахе читаем из БД и заполняем кэш. Для сильной консистентности чтения — Write-Through (пишем одновременно в кэш и БД). Всегда сначала коммитим БД, потом обновляем/инвалидируем кэш, чтобы не дать устаревшим данным просочиться.
IV. Истечение и вытеснение → TTL для каждого типа данных, чтобы держать кэш свежим, политики вытеснения (LRU/LFU) для управления памятью, negative caching для промахов, чтобы не перегружать БД. Ограничивай размер объектов, чтобы не создавать проблемы с памятью и сборкой мусора.
V. Масштабирование и «горячие ключи» → Масштабируй через consistent hashing и реплики для пропускной способности чтения. Многоуровневый кэш (edge→regional→local). Горячие ключи — локальные L1 кэши, отдельные кэши для горячих ключей, либо rate-limiting запросов.
VI. Надёжность и наблюдаемость → Предотвращай «каскады» промахов через request coalescing, короткие блокировки (SETNX + expiry) или serve-stale-while-revalidate с jittered TTL. Мониторь hit rate, latency, eviction, память и replication lag с алертами.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤4🔥2
Готовим себя к собеседованиям в FAANG как в настоящем университете
I. Структуры данных, алгоритмы и классические вопросы
II. Упор на Python, Java, C++, C, но есть и материалы по Go, JS, Rust, Ruby
III. Примеры задач с разбором оптимизаций
IV. Подготовка не только к техническим этапам, но и к общим/поведенческим интервью
Репозиторий настолько топовый, что его отметили даже разработчики из Google. И да, есть перевод на русский.💯
https://github.com/jwasham/coding-interview-university
👉 Java Portal
I. Структуры данных, алгоритмы и классические вопросы
II. Упор на Python, Java, C++, C, но есть и материалы по Go, JS, Rust, Ruby
III. Примеры задач с разбором оптимизаций
IV. Подготовка не только к техническим этапам, но и к общим/поведенческим интервью
Репозиторий настолько топовый, что его отметили даже разработчики из Google. И да, есть перевод на русский.
https://github.com/jwasham/coding-interview-university
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5🔥1🌭1
Если бы ты разрабатывал API, как бы ты дал клиентам выбор между синхронными и асинхронными вызовами, не дублируя одну и ту же логику дважды?
Многие разработчики попадают в ловушку дублирования методов -> один для синхронного использования, другой для асинхронного. На первый взгляд это кажется нормальным, но на деле удваивает работу по поддержке и может привести к скрытым багам, если один из путей обновить, а другой забыть.
Более умный подход заключается в том, чтобы один раз спроектировать основную логику и дать клиентам самим выбрать, как её использовать. Самый простой паттерн заключается в том, чтобы сделать асинхронную версию основным методом. Она может возвращать
Таким образом, у тебя есть только один кодовый путь для поддержки. Асинхронные клиенты получают неблокирующую производительность, синхронные получают удобство блокирующих вызовов, а API остаётся чистым и устойчивым к будущим изменениям.
👉 Золотое правило здесь -> логику пишем один раз, используем её двумя способами.
👉 Java Portal
Многие разработчики попадают в ловушку дублирования методов -> один для синхронного использования, другой для асинхронного. На первый взгляд это кажется нормальным, но на деле удваивает работу по поддержке и может привести к скрытым багам, если один из путей обновить, а другой забыть.
Более умный подход заключается в том, чтобы один раз спроектировать основную логику и дать клиентам самим выбрать, как её использовать. Самый простой паттерн заключается в том, чтобы сделать асинхронную версию основным методом. Она может возвращать
CompletableFuture
или реактивный тип вроде Mono в Spring. Клиенты, которым нужен асинхронный вызов, используют его напрямую. Клиенты, которым нужен синхронный вариант, просто вызывают .join() или .get().Таким образом, у тебя есть только один кодовый путь для поддержки. Асинхронные клиенты получают неблокирующую производительность, синхронные получают удобство блокирующих вызовов, а API остаётся чистым и устойчивым к будущим изменениям.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤3
Java движется вперёд и готовится к выпуску версии 25 ✊
В новом релизе уменьшили количество шаблонного кода, переработали работу с конструкторами и полностью отказались от поддержки старых систем.
Все ключевые изменения и полезные фишки версии 25 — в этой статье
👉 Java Portal
В новом релизе уменьшили количество шаблонного кода, переработали работу с конструкторами и полностью отказались от поддержки старых систем.
Все ключевые изменения и полезные фишки версии 25 — в этой статье
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11🔥5
This media is not supported in your browser
VIEW IN TELEGRAM
Сегодня отмечается День программиста!
256-й день года выбран не случайно:
С праздником, коллеги!☺️
👉 Java Portal
256-й день года выбран не случайно:
Дата праздника объясняется расчетом: 2 (двоичная система исчисления) в степени 8 (количество битов в байте). То есть 2^8= 256. Поэтому в обычный год день программиста 13 сентября, а в високосный — 12 сентября
С праздником, коллеги!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤13👍6🔥4
Если HashMap позволяет null, почему ConcurrentHashMap при тех же операциях кидает NullPointerException?
В HashMap можно хранить один ключ null и любое количество значений null. В ConcurrentHashMap любое использование null (ключ или значение) вызывает NullPointerException. Разница здесь в ясности и безопасности работы в многопоточной среде.
В однопоточном HashMap, если map.get(k) возвращает null, можно уточнить через map.containsKey(k). Этот двухшаговый подход работает, потому что между вызовами никто не меняет карту.
В многопоточном окружении такой трюк ломается. Поток A может вызвать containsKey(k) и увидеть ключ. До того как он вызовет get(k), поток B может удалить эту запись. Теперь get(k) вернёт null, и поток A не сможет понять, было ли значение реально null или запись исчезла между вызовами. Двухшаговая проверка не атомарна и ненадёжна.
Атомарные методы вроде putIfAbsent, computeIfAbsent и replace тоже используют null как “отсутствие отображения”. Если бы null был допустимым значением, эти методы становились бы неоднозначными или требовали бы обёрток и дополнительных проверок, усложняя API и реализацию.
Запрещая null в ключах и значениях, ConcurrentHashMap гарантирует, что get(k) == null всегда и однозначно означает “нет отображения”. Это упрощает API, делает семантику ясной и позволяет эффективно работать в многопоточном режиме.
👉 Java Portal
В HashMap можно хранить один ключ null и любое количество значений null. В ConcurrentHashMap любое использование null (ключ или значение) вызывает NullPointerException. Разница здесь в ясности и безопасности работы в многопоточной среде.
В однопоточном HashMap, если map.get(k) возвращает null, можно уточнить через map.containsKey(k). Этот двухшаговый подход работает, потому что между вызовами никто не меняет карту.
В многопоточном окружении такой трюк ломается. Поток A может вызвать containsKey(k) и увидеть ключ. До того как он вызовет get(k), поток B может удалить эту запись. Теперь get(k) вернёт null, и поток A не сможет понять, было ли значение реально null или запись исчезла между вызовами. Двухшаговая проверка не атомарна и ненадёжна.
Атомарные методы вроде putIfAbsent, computeIfAbsent и replace тоже используют null как “отсутствие отображения”. Если бы null был допустимым значением, эти методы становились бы неоднозначными или требовали бы обёрток и дополнительных проверок, усложняя API и реализацию.
Запрещая null в ключах и значениях, ConcurrentHashMap гарантирует, что get(k) == null всегда и однозначно означает “нет отображения”. Это упрощает API, делает семантику ясной и позволяет эффективно работать в многопоточном режиме.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍5
«В Algorithms Illuminated Тим Раффгарден объясняет основы алгоритмов максимально доступным способом.» – https://algorithmsilluminated.org
От умножения целых чисел до NP-полноты — 4 части, 100+ видео, каждое синхронизировано с разделами книги для глубокого погружения.
👉 Java Portal
От умножения целых чисел до NP-полноты — 4 части, 100+ видео, каждое синхронизировано с разделами книги для глубокого погружения.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Когда вы становитесь опытным Java-разработчиком, на собеседованиях нам уже недостаточно просто задавать фактические вопросы. На первое место выходят сценарные вопросы. Посмотрим на один такой сценарий и попробуем дать ответы.
Сценарий: Ваш сервис выступает как оркестратор, вызывая несколько downstream-сервисов для выполнения запроса. Один из этих сервисов известен своей ненадёжностью -> он медленный и иногда возвращает ошибку 503 Service Unavailable.
Вопросы:
I. Как сделать ваш сервис устойчивым к ненадёжности downstream-сервиса?
Объясните, как вы бы реализовали паттерн Circuit Breaker с помощью библиотеки вроде Resilience4j. Какие ключевые состояния у Circuit Breaker (Closed, Open, Half-Open)?
II. Какой fallback-механизм вы бы реализовали для случая, когда цепь открыта?
Например, вернёте ли вы кэшированные данные, дефолтный ответ или поставите запрос в очередь на последующую обработку?
III. Как вы бы спроектировали глобальную стратегию обработки исключений в приложении с использованием
Покажите, как сопоставить конкретное бизнес-исключение (например, ProductNotFoundException) с HTTP-статусом 404 Not Found и вернуть чистый JSON-ответ с ошибкой.
👉 Java Portal
Сценарий: Ваш сервис выступает как оркестратор, вызывая несколько downstream-сервисов для выполнения запроса. Один из этих сервисов известен своей ненадёжностью -> он медленный и иногда возвращает ошибку 503 Service Unavailable.
Вопросы:
I. Как сделать ваш сервис устойчивым к ненадёжности downstream-сервиса?
Объясните, как вы бы реализовали паттерн Circuit Breaker с помощью библиотеки вроде Resilience4j. Какие ключевые состояния у Circuit Breaker (Closed, Open, Half-Open)?
II. Какой fallback-механизм вы бы реализовали для случая, когда цепь открыта?
Например, вернёте ли вы кэшированные данные, дефолтный ответ или поставите запрос в очередь на последующую обработку?
III. Как вы бы спроектировали глобальную стратегию обработки исключений в приложении с использованием
@RestControllerAdvice
?Покажите, как сопоставить конкретное бизнес-исключение (например, ProductNotFoundException) с HTTP-статусом 404 Not Found и вернуть чистый JSON-ответ с ошибкой.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤3
This media is not supported in your browser
VIEW IN TELEGRAM
Находка для тех, кто учит SQL
sqlzap.com это онлайн-платформа для практики SQL-запросов прямо в браузере. По сути, интерактивный тренажёр, где можно отрабатывать навыки написания запросов.
Идеально, чтобы «набить руку» — что-то вроде LeetCode, только для SQL.
👉 Java Portal
sqlzap.com это онлайн-платформа для практики SQL-запросов прямо в браузере. По сути, интерактивный тренажёр, где можно отрабатывать навыки написания запросов.
Идеально, чтобы «набить руку» — что-то вроде LeetCode, только для SQL.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5
Стратегия в Java —> уходим от нагромождения if-else
Очень часто, когда начинаем писать код и описывать бизнес-логику, мы обклеиваем методы условными конструкциями.
Со временем, при изменении требований или росте сложности, таких условий становится всё больше.
В итоге код становится трудным для поддержки, его тяжело воспринимать, и он теряет черты объектно-ориентированного подхода.
Стратегия —> это поведенческий шаблон проектирования, позволяющий вынести изменяющееся поведение в отдельные классы и подставлять нужную реализацию во время исполнения программы. То есть вместо длинных if-else мы просто передаём объект с требуемой логикой.
Проблема -> громоздкие if-else
Такой код:
. трудно читать
. неудобно расширять
. легко сломать при добавлении новых веток
Решение через стратегию:
1. Описываем общий интерфейс:
2. Создаём реализации под разные типы клиентов:
3. Контекст, который применяет выбранную стратегию:
4. Подбор реализации во время выполнения:
Теперь каждая логика скидки инкапсулирована в своём классе. Если появляются новые условия, меняется только реализация конкретной стратегии, а не весь метод.
Даже выбор стратегии можно полностью отвязать от if-логики:
Таким образом, мы избавляемся от условных блоков и делаем код чище, гибче и проще в сопровождении.
👉 Java Portal
Очень часто, когда начинаем писать код и описывать бизнес-логику, мы обклеиваем методы условными конструкциями.
Со временем, при изменении требований или росте сложности, таких условий становится всё больше.
В итоге код становится трудным для поддержки, его тяжело воспринимать, и он теряет черты объектно-ориентированного подхода.
Стратегия —> это поведенческий шаблон проектирования, позволяющий вынести изменяющееся поведение в отдельные классы и подставлять нужную реализацию во время исполнения программы. То есть вместо длинных if-else мы просто передаём объект с требуемой логикой.
Проблема -> громоздкие if-else
public double calculateDiscount(String customerType, double amount) {
if (customerType.equals("regular")) {
return amount * 0.05;
} else if (customerType.equals("vip")) {
return amount * 0.1;
} else if (customerType.equals("super-vip")) {
return amount * 0.2;
} else {
return 0;
}
}
Такой код:
. трудно читать
. неудобно расширять
. легко сломать при добавлении новых веток
Решение через стратегию:
1. Описываем общий интерфейс:
public interface DiscountStrategy {
double applyDiscount(double amount);
}
2. Создаём реализации под разные типы клиентов:
public class RegularDiscount implements DiscountStrategy {
public double applyDiscount(double amount) {
return amount * 0.05;
}
}
public class VipDiscount implements DiscountStrategy {
public double applyDiscount(double amount) {
return amount * 0.1;
}
}
public class SuperVipDiscount implements DiscountStrategy {
public double applyDiscount(double amount) {
return amount * 0.2;
}
}
3. Контекст, который применяет выбранную стратегию:
public class DiscountService {
private final DiscountStrategy strategy;
public DiscountService(DiscountStrategy strategy) {
this.strategy = strategy;
}
public double getDiscountedPrice(double amount) {
return strategy.applyDiscount(amount);
}
}
4. Подбор реализации во время выполнения:
DiscountStrategy strategy = new VipDiscount();
DiscountService service = new DiscountService(strategy);
double price = service.getDiscountedPrice(1000); // 100.0
Теперь каждая логика скидки инкапсулирована в своём классе. Если появляются новые условия, меняется только реализация конкретной стратегии, а не весь метод.
Даже выбор стратегии можно полностью отвязать от if-логики:
Map<String, DiscountStrategy> strategies = Map.of(
"regular", new RegularDiscount(),
"vip", new VipDiscount(),
"super-vip", new SuperVipDiscount()
);
DiscountStrategy strategy = strategies.getOrDefault(customerType, amount -> 0.0);
double result = strategy.applyDiscount(1000);
Таким образом, мы избавляемся от условных блоков и делаем код чище, гибче и проще в сопровождении.
Please open Telegram to view this post
VIEW IN TELEGRAM
1❤17🔥5👍3
Ты опытный Java-разработчик. Что дальше?
Дальше — искать новые пути развития и обмениваться знаниями. Это можно сделать на Joker — крупнейшей Java-конференции в России.
📍 17–18 октября, Санкт-Петербург
Тебя ждут хардкорные доклады о современных практиках Java-разработки, новых фичах Java, нюансах Spring, работе JVM под капотом, Kotlin и Scala, ORM и о базах данных. Еще там будут обсуждать архитектуру и backend, нагрузки и производительность, DevOps — выбирай, что ближе по духу.
Помимо докладов будут дискуссии со спикерами, зоны нетворкинга, а в конце первого дня — вечеринка!
Подробности и билеты — на сайте Joker.
С промокодом JP10 персональный билет стоит дешевле.
Реклама. ООО "ДЖУГ РУ ГРУП". ИНН 7801341446.
Дальше — искать новые пути развития и обмениваться знаниями. Это можно сделать на Joker — крупнейшей Java-конференции в России.
📍 17–18 октября, Санкт-Петербург
Тебя ждут хардкорные доклады о современных практиках Java-разработки, новых фичах Java, нюансах Spring, работе JVM под капотом, Kotlin и Scala, ORM и о базах данных. Еще там будут обсуждать архитектуру и backend, нагрузки и производительность, DevOps — выбирай, что ближе по духу.
Помимо докладов будут дискуссии со спикерами, зоны нетворкинга, а в конце первого дня — вечеринка!
Подробности и билеты — на сайте Joker.
С промокодом JP10 персональный билет стоит дешевле.
Реклама. ООО "ДЖУГ РУ ГРУП". ИНН 7801341446.
❤3😁2🔥1🤯1
Пять эффективных схем кэширования, которые ускорят ваш микросервис
В современных микросервисных архитектурах кэширование играет ключевую роль в обеспечении высокой скорости работы, масштабируемости и устойчивости систем. Грамотное использование схем кэширования позволяет существенно снизить нагрузку на базы данных, сократить время отклика и повысить общую производительность системы.
Читать: habr.com
👉 Java Portal
В современных микросервисных архитектурах кэширование играет ключевую роль в обеспечении высокой скорости работы, масштабируемости и устойчивости систем. Грамотное использование схем кэширования позволяет существенно снизить нагрузку на базы данных, сократить время отклика и повысить общую производительность системы.
Читать: habr.com
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6
Ты всё ещё перебираешь списки в Java с помощью
Давай разберёмся, как они работают.
Streams в Java дают декларативный способ обработки данных. Вместо того чтобы писать внешние циклы, ты описываешь конвейер операций, который выполняется внутри потока.
Конвейер потока состоит из трёх частей:
I. Источник — начальная точка, обычно это коллекция, массив или I/O-канал.
Пример:
II. Промежуточные операции — преобразуют поток и возвращают новый поток. Популярные:
• filter → оставляет элементы, удовлетворяющие условию
• map → применяет функцию к каждому элементу
• sorted → сортирует поток
Эти операции ленивые — они не выполняются, пока не вызвана терминальная операция.
III. Терминальная операция — даёт финальный результат или побочный эффект. Примеры:
• collect → собирает элементы в коллекцию
• reduce → сводит элементы к одному значению
• forEach → перебирает элементы и выполняет действие
Потоки могут быть последовательными и параллельными. Последовательные обрабатывают элементы один за другим, а параллельные разбивают работу на несколько потоков. Это мощный инструмент для больших данных, но параллельные потоки стоит использовать осторожно — они добавляют накладные расходы.
К примеру, найти все чётные числа, возвести их в квадрат и собрать в новый список.
👉 Java Portal
for
или forEach
? Потоки позволяют писать короче, чище и читаемее.Давай разберёмся, как они работают.
Streams в Java дают декларативный способ обработки данных. Вместо того чтобы писать внешние циклы, ты описываешь конвейер операций, который выполняется внутри потока.
Конвейер потока состоит из трёх частей:
I. Источник — начальная точка, обычно это коллекция, массив или I/O-канал.
Пример:
numbers.stream()
II. Промежуточные операции — преобразуют поток и возвращают новый поток. Популярные:
• filter → оставляет элементы, удовлетворяющие условию
• map → применяет функцию к каждому элементу
• sorted → сортирует поток
Эти операции ленивые — они не выполняются, пока не вызвана терминальная операция.
III. Терминальная операция — даёт финальный результат или побочный эффект. Примеры:
• collect → собирает элементы в коллекцию
• reduce → сводит элементы к одному значению
• forEach → перебирает элементы и выполняет действие
Потоки могут быть последовательными и параллельными. Последовательные обрабатывают элементы один за другим, а параллельные разбивают работу на несколько потоков. Это мощный инструмент для больших данных, но параллельные потоки стоит использовать осторожно — они добавляют накладные расходы.
К примеру, найти все чётные числа, возвести их в квадрат и собрать в новый список.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12
This media is not supported in your browser
VIEW IN TELEGRAM
GPU обрабатывают тысячи пикселей параллельно, но что происходит, когда мы записываем всего один пиксель в буфер кадра? Как это реализуется?
Посмотри, как CPU "рисует" каждый пиксель в этом наглядном визуальном симуляторе RISC-V (загрузи пример с bitmap):
https://eseo-tech.github.io/emulsiV/
👉 Java Portal
Посмотри, как CPU "рисует" каждый пиксель в этом наглядном визуальном симуляторе RISC-V (загрузи пример с bitmap):
https://eseo-tech.github.io/emulsiV/
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9
This media is not supported in your browser
VIEW IN TELEGRAM
Java 25 уже доступна!
Это важнейшее событие этого года в экосистеме Java⌨️
Особенность этого выпуска в том, что у некоторых производителей он представлен как LTS-версия, а значит обновления для неё будут выходить минимум 5 лет с момента релиза и до сентября 2030 года.
Ниже приведены ключевые нововведения
I. Компактные сорс-файлы и instance-методы main()
Теперь можно писать код без полного объявления класса и метода main
JVM автоматически создаёт неявный класс, в который помещает метод main() и другие верхнеуровневые объявления.
II. Module Import Declarations
Инструкция
III. Flexible Constructor Bodies
Позволяет писать инструкции в конструкторе до вызова super() или this().
IV. Удалён 32-битный x86-порт OpenJDK
Из исходников исключены все части, отвечающие за поддержку 32-bit x86. Сборка JDK под эту архитектуру теперь невозможна.
V. Scoped Values
Класс
VI. Key Derivation Function API
Функции выработки ключей (KDF) позволяют создавать криптографически стойкие секретные ключи (например, AES) на основе исходного материала (например, пароля) и дополнительных данных (например, соли).
Полный список изменений доступен здесь - https://jdk.java.net/25/release-notes
👉 Java Portal
Это важнейшее событие этого года в экосистеме Java
Особенность этого выпуска в том, что у некоторых производителей он представлен как LTS-версия, а значит обновления для неё будут выходить минимум 5 лет с момента релиза и до сентября 2030 года.
Ниже приведены ключевые нововведения
I. Компактные сорс-файлы и instance-методы main()
Теперь можно писать код без полного объявления класса и метода main
String greeting = "Hello, World!";
void main() {
System.out.println(greeting);
}
JVM автоматически создаёт неявный класс, в который помещает метод main() и другие верхнеуровневые объявления.
II. Module Import Declarations
Инструкция
import module M
равнозначна импорту всех экспортируемых пакетов из модуля M
и его транзитивных зависимостей в текущий модуль.III. Flexible Constructor Bodies
Позволяет писать инструкции в конструкторе до вызова super() или this().
IV. Удалён 32-битный x86-порт OpenJDK
Из исходников исключены все части, отвечающие за поддержку 32-bit x86. Сборка JDK под эту архитектуру теперь невозможна.
V. Scoped Values
Класс
ScopedValue
позволяет обмениваться неизменяемыми данными без передачи их через аргументы методов. Это альтернатива ThreadLocal
. Оба механизма решают одну задачу — перенос значения переменной внутри потока или дерева потоков без явного параметра.VI. Key Derivation Function API
Функции выработки ключей (KDF) позволяют создавать криптографически стойкие секретные ключи (например, AES) на основе исходного материала (например, пароля) и дополнительных данных (например, соли).
Полный список изменений доступен здесь - https://jdk.java.net/25/release-notes
Please open Telegram to view this post
VIEW IN TELEGRAM
❤13👍8🔥4