Please open Telegram to view this post
VIEW IN TELEGRAM
Базовые приёмы экономии памяти в Java
1. Ленивая инициализация "тяжёлых" полей
– Используйте lazy initialization, чтобы откладывать создание объекта до момента, когда он действительно понадобится.
2. Избегайте анонимных внутренних классов в циклах
– Каждое такое использование создаёт объект внутреннего класса — это скрытая нагрузка на память.
3. Используйте статические фабричные методы
– Они позволяют экономить память за счёт повторного использования уже созданных экземпляров, вместо создания новых при каждом вызове.
4. Применяйте метод intern()
– Метод
Используя
5. Используйте паттерн Flyweight для повторяющихся неизменяемых данных
– Экономит память за счёт разделения общих экземпляров (используется, например, в
👉 Java Portal
1. Ленивая инициализация "тяжёлых" полей
– Используйте lazy initialization, чтобы откладывать создание объекта до момента, когда он действительно понадобится.
2. Избегайте анонимных внутренних классов в циклах
– Каждое такое использование создаёт объект внутреннего класса — это скрытая нагрузка на память.
3. Используйте статические фабричные методы
– Они позволяют экономить память за счёт повторного использования уже созданных экземпляров, вместо создания новых при каждом вызове.
4. Применяйте метод intern()
– Метод
intern()
класса String возвращает каноническое представление строки.Используя
intern()
, можно гарантировать, что одна и та же строка будет представлена в памяти в виде единственного объекта, даже если она создаётся многократно.String s1 = "hello";
String s2 = "hello".intern();
5. Используйте паттерн Flyweight для повторяющихся неизменяемых данных
– Экономит память за счёт разделения общих экземпляров (используется, например, в
Integer.valueOf()
, Boolean.TRUE
и т.п.).Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Проектирование Backend-системы. Система уведомлений
Цель —> Разработать систему для доставки уведомлений пользователям через:
➣ Встроенные уведомления
➣ Push-уведомления (мобильные/веб)
➣ Email
С поддержкой:
✓Масштабируемости (миллионы пользователей)
✓Настраиваемых пользовательских предпочтений
✓Надежности, повторных попыток и дедупликации
✓Реального времени и пакетных оповещений
Пошаговый сквозной процесс
Шаг 1: Срабатывание события
✓ Приложение инициирует событие:
➣ Например, пользователь A лайкнул ваш пост
➣ Отправляется запрос POST на API /notify:
Шаг 2: Добавление в очередь уведомлений
➣ API валидирует данные и помещает сообщение в очередь уведомлений (Kafka/RabbitMQ)
Шаг 3: Обработка воркером
✓ Воркер извлекает сообщение из очереди:
➣ Получает полную информацию о событии
➣ Обращается к сервису предпочтений:
- Подписан ли UserB на уведомления типа "like"?
- Хочет email или только in-app?
✓ Создает payload уведомления, например:
✓
Шаг 4: Рассылка по каналам
✓ На основе предпочтений воркер запускает соответствующие каналы:
➣ In-App Writer → сохраняет в БД (для отображения иконки колокольчика)
➣ Push-сервис → отправляет push (мобильный/веб) через FCM/APNs
➣ Email-сервис → ставит email в очередь (через SES/SendGrid)
Шаг 5: Отправка и повторные попытки
✓ Сервисы каналов:
➣ Декуплированы для изоляции отказов
➣ Повторяют попытки при сбоях (3–5 раз с экспоненциальной задержкой)
➣ Логируют статус (отправлено, ошибка, пропущено)
Шаг 6: Отображение in-app уведомлений
✓ UI вызывает /notifications?user=UserB
✓ Возвращаются N последних уведомлений из БД:
Функции надежности
✓ Гарантированная доставка хотя бы один раз с идемпотентной записью
✓ Повторные попытки с экспоненциальной задержкой
✓ DLQ (Dead Letter Queue) для сообщений с ошибками
✓ Метрики: успех/неудача на сообщение
✓ Алерты при переполнении очереди или всплесках ошибок
👉 Java Portal
Цель —> Разработать систему для доставки уведомлений пользователям через:
➣ Встроенные уведомления
➣ Push-уведомления (мобильные/веб)
С поддержкой:
✓Масштабируемости (миллионы пользователей)
✓Настраиваемых пользовательских предпочтений
✓Надежности, повторных попыток и дедупликации
✓Реального времени и пакетных оповещений
Пошаговый сквозной процесс
Шаг 1: Срабатывание события
✓ Приложение инициирует событие:
➣ Например, пользователь A лайкнул ваш пост
➣ Отправляется запрос POST на API /notify:
{
"event_type": "like",
"actor": "UserA",
"receiver": "UserB",
"object": "Post123"
}
Шаг 2: Добавление в очередь уведомлений
➣ API валидирует данные и помещает сообщение в очередь уведомлений (Kafka/RabbitMQ)
Шаг 3: Обработка воркером
✓ Воркер извлекает сообщение из очереди:
➣ Получает полную информацию о событии
➣ Обращается к сервису предпочтений:
- Подписан ли UserB на уведомления типа "like"?
- Хочет email или только in-app?
✓ Создает payload уведомления, например:
✓
"UserA лайкнул ваш пост 'How to scale systems'"
Шаг 4: Рассылка по каналам
✓ На основе предпочтений воркер запускает соответствующие каналы:
➣ In-App Writer → сохраняет в БД (для отображения иконки колокольчика)
➣ Push-сервис → отправляет push (мобильный/веб) через FCM/APNs
➣ Email-сервис → ставит email в очередь (через SES/SendGrid)
Шаг 5: Отправка и повторные попытки
✓ Сервисы каналов:
➣ Декуплированы для изоляции отказов
➣ Повторяют попытки при сбоях (3–5 раз с экспоненциальной задержкой)
➣ Логируют статус (отправлено, ошибка, пропущено)
Шаг 6: Отображение in-app уведомлений
✓ UI вызывает /notifications?user=UserB
✓ Возвращаются N последних уведомлений из БД:
[
{ "text": "UserA лайкнул ваш пост", "read": false, ... }
]
Функции надежности
✓ Гарантированная доставка хотя бы один раз с идемпотентной записью
✓ Повторные попытки с экспоненциальной задержкой
✓ DLQ (Dead Letter Queue) для сообщений с ошибками
✓ Метрики: успех/неудача на сообщение
✓ Алерты при переполнении очереди или всплесках ошибок
Please open Telegram to view this post
VIEW IN TELEGRAM
Хаки Java-кодинга, которые стоит знать разработчикам
✓ Используйте
➣ Почему: короче, безопаснее, неизменяемо — снижает риск случайной мутации.
✓ Используйте
➣ Почему: устраняет проверки на
✓ Используйте
➣ Почему: чище и удобнее для сериализации/десериализации enum'ов в API, БД или файлах.
✓ Используйте
➣ Почему: читаемо, понятно, без ошибок в условных сравнениях.
✓ Используйте
➣ Почему: выразительнее и чище — больше никаких ручных
👉 Java Portal
✓ Используйте
Map.ofEntries()
для создания неизменяемых мап с несколькими парами➣ Почему: короче, безопаснее, неизменяемо — снижает риск случайной мутации.
✓ Используйте
Optional.map().orElse()
вместо ручной обработки null
➣ Почему: устраняет проверки на
null
, читается лучше, легко комбинируется с другой логикой.✓ Используйте
Enum.valueOf()
+ name()
для безопасного преобразования enum
➣ Почему: чище и удобнее для сериализации/десериализации enum'ов в API, БД или файлах.
✓ Используйте
Comparator.comparing().thenComparing()
вместо кастомной логики➣ Почему: читаемо, понятно, без ошибок в условных сравнениях.
✓ Используйте
Optional.ifPresentOrElse()
вместо if-else
с isPresent()
➣ Почему: выразительнее и чище — больше никаких ручных
get()
и нагромождений if-else
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Некоторые редкие, но ценные паттерны в Java
1. Null Object Pattern (Объект-Заглушка вместо null)
❯ Позволяет избежать проверок на null, возвращая объект с "пустым" поведением, реализующий ожидаемый интерфейс.
❯ Помогает держать код чистым, без множества
❯ Используй, когда возврат
2. Parameter Object Pattern (Объект параметров)
❯ Объединяет логически связанные параметры в один объект.
❯ Такой объект удобно прокидывать через разные уровни приложения, обеспечивая согласованность.
❯ Используй, когда у метода слишком много связанных аргументов.
3. Fluent Interface / Method Chaining (Флюент-интерфейс / Цепочка вызовов)
❯ Позволяет вызывать методы цепочкой, улучшая читаемость и выражая намерение кода (особенно в билдерах и конфигурациях).
❯ Используй в билдерах или для декларативной настройки.
4. Execute Around Method Pattern (Шаблон обёртки вокруг действия)
❯ Инкапсулирует логику подготовки и завершения вокруг основного действия.
❯ Гарантирует корректное освобождение ресурсов.
❯ Используй для управления ресурсами (файлы, БД и т.п.).
5. Initialization-on-Demand Holder (ленивый Singleton без
❯ Безопасный, потокобезопасный и производительный способ ленивой инициализации Singleton'а без явной синхронизации.
❯ Используй, если нужен быстрый и потокобезопасный синглтон.
👉 Java Portal
1. Null Object Pattern (Объект-Заглушка вместо null)
❯ Позволяет избежать проверок на null, возвращая объект с "пустым" поведением, реализующий ожидаемый интерфейс.
❯ Помогает держать код чистым, без множества
if (obj != null)
перед каждым вызовом метода.❯ Используй, когда возврат
null
приводит к захламлению условными конструкциями.2. Parameter Object Pattern (Объект параметров)
❯ Объединяет логически связанные параметры в один объект.
❯ Такой объект удобно прокидывать через разные уровни приложения, обеспечивая согласованность.
❯ Используй, когда у метода слишком много связанных аргументов.
3. Fluent Interface / Method Chaining (Флюент-интерфейс / Цепочка вызовов)
❯ Позволяет вызывать методы цепочкой, улучшая читаемость и выражая намерение кода (особенно в билдерах и конфигурациях).
❯ Используй в билдерах или для декларативной настройки.
4. Execute Around Method Pattern (Шаблон обёртки вокруг действия)
❯ Инкапсулирует логику подготовки и завершения вокруг основного действия.
❯ Гарантирует корректное освобождение ресурсов.
❯ Используй для управления ресурсами (файлы, БД и т.п.).
5. Initialization-on-Demand Holder (ленивый Singleton без
synchronized
)❯ Безопасный, потокобезопасный и производительный способ ленивой инициализации Singleton'а без явной синхронизации.
❯ Используй, если нужен быстрый и потокобезопасный синглтон.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Разные подходы к структуре REST API:
1) Чистый CRUD-стиль
👍 Просто и предсказуемо
👎 Начинает хромать при действиях вне CRUD
2) Эндпоинты-действия
👍 Ясное указание на сайд-эффекты
👎 Нарушает REST-идеи (глаголы в URL)
3) Вложенные ресурсы
👍 Удобно моделирует связи между сущностями
👎 Может вырасти в избыточную иерархию, тяжело поддерживать
4) Плоская структура + фильтры
👍 Гибкие запросы
👎 Требует строгой валидации query-параметров
5) Версионирование API
👍 Безопасное развитие API
👎 Увеличивает накладные расходы на поддержку
👉 Java Portal
1) Чистый CRUD-стиль
GET /users
POST /users
PUT /users/{id}
DELETE /users/{id}
2) Эндпоинты-действия
POST /users/{id}/deactivate
POST /orders/{id}/cancel
3) Вложенные ресурсы
GET /users/{id}/orders
POST /users/{id}/addresses
4) Плоская структура + фильтры
GET /orders?userId=123
GET /products?category=books&page=2
5) Версионирование API
GET /v1/users
Accept: application/vnd.myapp.v2+json
Please open Telegram to view this post
VIEW IN TELEGRAM
Плохие практики в коде, которые кажутся безобидными (но это не так)
1) Чрезмерное использование private-методов, скрывающее сложность
Когда private-методов слишком много, это ломает читаемость и разносит логику по коду. Визуально всё выглядит «чисто», но важные логические шаги оказываются скрытыми.
Когда это плохо:
Вы выносите код в private-методы просто ради самого факта выноса, а не потому что они переиспользуемы или несут отдельный смысл.
Как лучше:
Выделяйте разные зоны ответственности в отдельные классы или сервисы, которые сотрудничают между собой.
2) Автоматическое добавление private + геттеров/сеттеров по умолчанию
Бездумное добавление геттеров и сеттеров превращает ООП в просто «структуры с методами». Это нарушает инкапсуляцию, а не сохраняет её.
Вместо этого:
● Открывайте только те геттеры/сеттеры, которые реально нужны для бизнес-логики.
● Вместо сеттеров лучше использовать методы, которые выполняют осмысленные действия с валидацией.
3) Чрезмерное использование static-методов и переменных
static-методы кажутся удобными, но убивают тестируемость и гибкость. Они привязывают код к жёсткому глобальному состоянию.
Как лучше:
Используйте внедрение зависимостей или передавайте нужную конфигурацию явно туда, где она требуется.
4) Слишком большой класс (“God Object”)
Огромный класс, который делает всё подряд, превращается в чёрный ящик — его сложно тестировать, менять и понимать.
Как лучше:
● Применяйте принцип единственной ответственности (Single Responsibility Principle): у класса должна быть только одна причина для изменения.
● Разделяйте обязанности между меньшими по размеру классами или модулями.
5) Скрытие семантики null / Optional
Возврат null или скрытое использование Optional приводит к неожиданным ошибкам во время выполнения. Отсутствие или наличие значения должно быть выражено явно.
Как лучше:
● Используйте
● Если это действительно ошибка — выбрасывайте чётко определённое исключение.
👉 Java Portal
1) Чрезмерное использование private-методов, скрывающее сложность
Когда private-методов слишком много, это ломает читаемость и разносит логику по коду. Визуально всё выглядит «чисто», но важные логические шаги оказываются скрытыми.
Когда это плохо:
Вы выносите код в private-методы просто ради самого факта выноса, а не потому что они переиспользуемы или несут отдельный смысл.
Как лучше:
Выделяйте разные зоны ответственности в отдельные классы или сервисы, которые сотрудничают между собой.
2) Автоматическое добавление private + геттеров/сеттеров по умолчанию
Бездумное добавление геттеров и сеттеров превращает ООП в просто «структуры с методами». Это нарушает инкапсуляцию, а не сохраняет её.
Вместо этого:
● Открывайте только те геттеры/сеттеры, которые реально нужны для бизнес-логики.
● Вместо сеттеров лучше использовать методы, которые выполняют осмысленные действия с валидацией.
3) Чрезмерное использование static-методов и переменных
static-методы кажутся удобными, но убивают тестируемость и гибкость. Они привязывают код к жёсткому глобальному состоянию.
Как лучше:
Используйте внедрение зависимостей или передавайте нужную конфигурацию явно туда, где она требуется.
4) Слишком большой класс (“God Object”)
Огромный класс, который делает всё подряд, превращается в чёрный ящик — его сложно тестировать, менять и понимать.
Как лучше:
● Применяйте принцип единственной ответственности (Single Responsibility Principle): у класса должна быть только одна причина для изменения.
● Разделяйте обязанности между меньшими по размеру классами или модулями.
5) Скрытие семантики null / Optional
Возврат null или скрытое использование Optional приводит к неожиданным ошибкам во время выполнения. Отсутствие или наличие значения должно быть выражено явно.
Как лучше:
● Используйте
Optional<T>
или другой задокументированный способ, чтобы показать, что результат может отсутствовать.● Если это действительно ошибка — выбрасывайте чётко определённое исключение.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM