Библиотека Java разработчика
10.5K subscribers
1.17K photos
594 videos
58 files
1.51K links
📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate.


По всем вопросам @evgenycarter

РКН clck.ru/3KoGeP
Download Telegram
📦 От Кода к Продакшену: JAR и Docker

В старые времена (Java EE) процесс деплоя был адом: нужно было установить на сервер Tomcat, настроить его, скомпилировать .war файл, закинуть его в папку... 🤯

Spring Boot принес концепцию Fat JAR (Жирный JAR).

🍔 1. Fat JAR - "Всё своё ношу с собой"

Spring Boot упаковывает ваше приложение, все библиотеки (зависимости) и даже сам веб-сервер (Tomcat) в один единственный файл .jar.

Этот файл работает как .exe в Windows. Ему ничего не нужно, кроме установленной Java.

Как собрать?
В терминале (в папке проекта):


# Для Maven
./mvnw clean package

# Для Gradle
./gradlew build



В папке target (или build/libs) появится файл myapp-0.0.1-SNAPSHOT.jar.

Как запустить?
Где угодно (на сервере, на ноутбуке друга), где есть Java:


java -jar myapp.0.0.1-SNAPSHOT.jar



Всё! Сервер стартует.



🐳 2. Docker - "Работает везде"

JAR это хорошо. Но что, если на сервере стоит Java 11, а у вас Java 17? Или другая ОС? Начинается проблема "На моем компьютере работало!".

Docker решает это, упаковывая ваше приложение вместе с Java и операционной системой в Контейнер.

Пишем Dockerfile
Создайте файл без расширения с именем Dockerfile в корне проекта.

Вариант для новичков (Простой):


# 1. Берем базовый образ с Java 17 (легковесный Alpine Linux)
FROM eclipse-temurin:17-jre-alpine

# 2. Копируем наш JAR внутрь образа
# (Предварительно нужно сделать mvn package руками!)
COPY target/*.jar app.jar

# 3. Говорим, какую команду запустить при старте контейнера
ENTRYPOINT ["java", "-jar", "/app.jar"]



Как запустить:


# 1. Собираем образ (Image)
docker build -t my-spring-app .

# 2. Запускаем контейнер
# -p 8080:8080 пробрасывает порт наружу
docker run -p 8080:8080 my-spring-app





🏗 3. Multi-stage Build (Уровень Pro)

В варианте выше вам нужно сначала собрать JAR руками. Это неудобно для CI/CD.
В профессиональном Dockerfile мы делаем сборку внутри Docker.


# --- Этап 1: Сборка (Build) ---
FROM maven:3.8.5-openjdk-17 AS builder
WORKDIR /app
COPY . .
# Собираем JAR, пропуская тесты (для скорости)
RUN mvn clean package -DskipTests

# --- Этап 2: Запуск (Run) ---
# Берем чистый, маленький образ для запуска
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# Копируем ТОЛЬКО готовый jar из первого этапа
COPY --from=builder /app/target/*.jar app.jar

ENTRYPOINT ["java", "-jar", "app.jar"]



Почему это круто? В финальном образе нет исходного кода и тяжелого Maven. Только Java и ваш JAR. Образ весит минимум, а собрать его можно одной командой docker build, даже если на компьютере вообще не установлена Java!

🔥 Итог

1. Maven/Gradle собирают код в один Fat JAR.
2. Dockerfile описывает среду для запуска.
3. Multi-stage build позволяет собирать и запускать приложение в изолированной среде, не засоряя систему.

#SpringBoot #Docker #DevOps #Deployment #Java

📲 Мы в MAX

👉@BookJava
👍62👎1
🧩 Микросервисы: Укрощение хаоса (Spring Cloud)

Когда у вас один сервис, всё просто. Но когда их становится 10, 20 или 50, возникают вопросы:

1. Как сервису А узнать IP-адрес сервиса Б? (А если он меняется динамически?)
2. Как не писать тонны кода для HTTP-запросов?
3. Как клиенту (фронтенду) обращаться ко всей этой куче сервисов?

Для этого есть три главных инструмента.

1️⃣ Eureka: Телефонная книга (Service Discovery)

В облаке сервисы постоянно перезапускаются, меняют IP-адреса и порты. Хардкодить http://localhost:8081 в коде - самоубийство.

Eureka Server - это реестр.

🔴Когда сервис (например, OrderService) запускается, он звонит в Eureka: "Привет, я OrderService, мой IP такой-то".

🔴Когда UserService хочет найти заказы, он спрашивает Eureka: "Где сейчас живет OrderService?".

В коде:
Вам нужно просто добавить аннотацию @EnableDiscoveryClient, и магия произойдет сама. Сервисы будут находить друг друга по имени, а не по IP.

2️⃣ OpenFeign: Магия общения

Окей, адрес мы нашли. Теперь нужно отправить запрос.
Раньше мы использовали RestTemplate - это было громоздко и некрасиво.
Feign позволяет вызывать удаленный REST-сервис так, будто это обычный метод интерфейса в вашем коде.

Было (RestTemplate):


String url = "http://order-service/orders/" + userId;
List<Order> orders = restTemplate.getForObject(url, List.class); // Фу, нетипизированно



Стало (FeignClient):
Вы просто пишете интерфейс, а реализацию Spring сгенерирует сам!


@FeignClient(name = "order-service") // Имя сервиса в Eureka
public interface OrderClient {

@GetMapping("/orders/{userId}")
List<Order> getUserOrders(@PathVariable Long userId);
}

// Использование в сервисе:
// List<Order> orders = orderClient.getUserOrders(123L);



Это называется Декларативный REST-клиент. Чисто, красиво, типизировано.

3️⃣ API Gateway: Единая точка входа

Представьте, что у вас 50 микросервисов. Фронтенд не должен знать адреса каждого из них (api.com:8081, api.com:8082...). Это небезопасно и сложно (CORS error, привет 👋).

Spring Cloud Gateway - это вахтер на входе.
Весь внешний мир стучится только в него (например, на порт 8080), а он уже сам разруливает, куда отправить запрос.

Что он делает:

1. Маршрутизация: /users/** -> лети в UserService, /orders/** -> лети в OrderService.

2. Безопасность: Проверяет JWT токен один раз на входе.

3. Rate Limiting: "Не больше 10 запросов в секунду от этого юзера".

Пример конфига (application.yaml):


spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://USER-SERVICE # lb = Load Balancer (через Eureka)
predicates:
- Path=/users/**



Итог: Как это работает вместе?

1. Сервисы просыпаются и регистрируются в Eureka.

2. Фронтенд шлет запрос на Gateway.

3. Gateway спрашивает у Eureka адрес нужного сервиса и пересылает запрос.

4. Если сервисам нужно пообщаться между собой, они используют Feign.

#Java #Microservices #SpringCloud #Eureka #Feign

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
🤝6👍5
📨 Apache Kafka: Нервная система микросервисов

Представьте, что вы заказали пиццу.

🔴REST подход: Вы стоите у прилавка и смотрите на повара, пока он не закончит. Вы не можете отойти. Если повар уснул - вы застряли.
🔴Kafka подход: Вы бросаете чек в коробку "Заказы" и уходите по своим делам. Повар возьмет заказ, когда освободится. Когда пицца будет готова, он положит её на стол выдачи, и вы получите уведомление.

Kafka - это распределенный лог событий. Это не просто очередь, это история всего, что произошло в системе.

🧩 Основные понятия

1. Topic (Топик) - Это "папка" или канал, куда падают сообщения. Например: orders-topic, email-topic.

2. Producer (Продюсер) - Тот, кто пишет сообщения в топик (например, сервис Заказов).

3. Consumer (Консьюмер) - Тот, кто читает сообщения (например, сервис Уведомлений или Склад).

4. Broker (Брокер) - Сервер Kafka, который хранит эти данные.

🚀 Главная фишка: Fire and Forget

Когда OrderService создает заказ, ему плевать, работает ли сейчас сервис отправки SMS или сервис начисления бонусов.
Он просто кидает событие OrderCreated в Kafka и мгновенно возвращает ответ пользователю "Заказ принят".

Сервисы-подписчики (Consumers) разгребут эти сообщения в своем темпе. Если сервис SMS упал, он поднимется через час, прочитает топик с того места, где остановился, и дошлет все смски. Данные не пропадут.

💻 Spring Kafka: Как писать код?

В Spring Boot работа с Kafka максимально упрощена.

1. Настройка (application.yaml)


spring:
kafka:
bootstrap-servers: localhost:9092 # Адрес брокера
consumer:
group-id: my-group # Важно для масштабирования



2. Продюсер (Отправляем сообщение)
Нам нужен бин KafkaTemplate.


@Service
@RequiredArgsConstructor
public class OrderProducer {

private final KafkaTemplate<String, String> kafkaTemplate;

public void sendOrderEvent(String orderId) {
// Отправляем в топик "orders"
kafkaTemplate.send("orders", orderId);
System.out.println("Сообщение отправлено: " + orderId);
}
}



3. Консьюмер (Слушаем эфир)
Просто вешаем аннотацию @KafkaListener.


@Service
public class NotificationConsumer {

@KafkaListener(topics = "orders", groupId = "notification_group")
public void listen(String orderId) {
System.out.println("Получено событие для заказа: " + orderId);
// Тут логика отправки email/sms
sendEmail(orderId);
}
}



Kafka vs RabbitMQ (Коротко)

Частый холивар.

🔴RabbitMQ - это классическая Очередь. Сообщение прочитали -> оно удалилось. Это "умный брокер, глупые потребители".
🔴Kafka - это Лог. Сообщения хранятся на диске днями (или вечно). Их можно перечитывать заново (Replay). Это идеально для аналитики и восстановления данных.

🔥 Итог

Kafka позволяет микросервисам быть слабосвязанными (decoupled).

🔴Сервис А не знает о существовании Сервиса Б.
🔴Система выдерживает пиковые нагрузки (Kafka просто буферизирует сообщения, пока консьюмеры не разгребут их).

#Java #Kafka #Microservices #SpringCloud #Architecture

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍82🔥2
🎥 Открытый урок «Eclipse Memory Analyzer (MAT): помощь в работе с heap».

🗓 04 февраля в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса
«Java Developer. Advanced».

Без MAT сложно найти причины OutOfMemoryError и утечек. Разберём базу и полезные приёмы анализа heap dump.


Что будет на вебинаре:
✔️ Ключевые концепции работы с MAT и навигация по heap dump.
✔️ Экспресс-анализ дампа памяти: где искать утечки и рост объектов.
✔️ Разбор реального дампа в онлайне на предмет memory leak.

В результате вебинара:
Вы сможете самостоятельно проводить анализ дампов памяти. Будете знать возможности MAT и какие задачи решаются с его помощью.

Кому будет интересно:
Подойдёт Java-разработчикам, инженерам сопровождения и SRE, кто сталкивается с OutOfMemoryError и падениями по памяти.

🔗 Ссылка на регистрацию: https://vk.cc/cU0oou

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
🚀 Redis + Spring Cache: Турбо-наддув для бэкенда

Самая медленная часть любого приложения это ввод-вывод (I/O). Поход в базу данных (Postgres/MySQL) это "долго" (миллисекунды). Поход в оперативную память это "мгновенно" (наносекунды).

Redis - это база данных, которая хранит всё в оперативной памяти (In-Memory). Она идеально подходит на роль кэша.

🧠 Как работает Spring Cache?

Spring предоставляет крутую абстракцию. Вам не нужно писать код для подключения к Redis в каждом методе. Вы просто вешаете аннотации, а Spring сам перехватывает вызов метода.

Алгоритм @Cacheable:

1. Вызывается метод getUser(1).

2. Spring лезет в кэш (Redis) по ключу user::1.

3. Если данные есть (Cache Hit): Spring НЕ выполняет код метода, а сразу возвращает данные из кэша.

4. Если данных нет (Cache Miss): Spring выполняет метод (идет в БД), берет результат, кладет его в кэш и отдает вам.

🛠 Настройка (2 шага)

1. Зависимости
В pom.xml добавляем стартер для кэша и драйвер Redis:


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>



2. Включаем рубильник
Над главным классом вешаем @EnableCaching.

💻 Магия аннотаций

У нас есть 3 главные аннотации, которые вы обязаны знать:

1. @Cacheable — "Запомни меня"

Вешаем над методами поиска (get, find).


@Service
public class UserService {

@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
// Имитация долгого запроса в БД (3 секунды)
simulateSlowService();
return userRepository.findById(id).orElseThrow();
}
}



Результат: Первый вызов займет 3 секунды. Второй, третий и тысячный вызов с тем же ID займут 0.001 секунды.

2. @CacheEvict - "Забудь меня"

Самая большая проблема кэширования - инвалидация. Если мы обновили данные юзера в БД, а в кэше осталась старая версия — это баг.
При обновлении или удалении мы должны очистить кэш.


@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}



Теперь при следующем вызове getUserById(id) Spring увидит, что кэш пуст, и снова сходит в БД за свежими данными.

3. @CachePut - "Обнови меня"

Используется реже. Метод выполняется всегда, а его результат кладется в кэш, обновляя старое значение.

🆚 Redis vs HashMap

"Зачем мне Redis, если я могу создать ConcurrentHashMap прямо в Java?"

1. Память: Если данных много, HashMap съест всю память JVM, и приложение упадет (OutOfMemory). Redis живет отдельно.

2. Микросервисы: Если у вас запущено 3 экземпляра сервиса, у каждого будет свой HashMap. Кэш будет рассинхронизирован. Redis это общий кэш для всех инстансов.

3. Живучесть: При перезагрузке приложения HashMap очищается. Redis (обычно) работает на отдельном сервере и хранит данные даже при рестарте вашего кода.

⚠️ Важный нюанс: Serializable

Чтобы положить Java-объект в Redis, его нужно превратить в байты (сериализовать).
Ваши DTO и Entity должны реализовывать интерфейс Serializable, либо (что правильнее) нужно настроить Jackson Serializer, чтобы хранить данные в Redis в читаемом JSON-формате.

🔥 Итог: кэширование - это самый простой способ масштабирования.

🔴Часто читаете, редко меняете? -> @Cacheable.
🔴Удалили/Обновили? -> @CacheEvict.

#Java #Spring #Redis #Caching #Performance

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
👮‍♂️ Spring Security: Фейсконтроль для вашего API

Spring Security - это не просто библиотека, это мощный фреймворк, который встает стеной между интернетом и вашим контроллером.

Его работа строится на концепции Filter Chain (Цепочка фильтров). Каждый запрос проходит через серию проверок: "Есть ли токен?", "Валиден ли он?", "Есть ли права?".

🔑 Authentication vs Authorization

Два слова, которые путают все джуниоры.

1. Authentication (Аутентификация): "Кто ты?"
🔴Ввод логина/пароля.
🔴Проверка отпечатка пальца.
🔴Ответ: 401 Unauthorized (если не знаем, кто это).


2. Authorization (Авторизация): "А что тебе можно?"
🔴Ты Вася (мы тебя узнали), но ты хочешь удалить базу данных. Тебе нельзя.
🔴Ответ: 403 Forbidden (знаем кто ты, но не пустим).



🎫 JWT (JSON Web Token) - Паспорт туриста

В микросервисах мы не храним состояние пользователя на сервере (Stateless). Вместо этого, при логине мы выдаем пользователю Токен.

JWT - это строка из трех частей, разделенных точками: Header.Payload.Signature.

🔴Payload: Полезные данные (User ID, Role, Email).
🔴Signature: Цифровая подпись сервера. Гарантирует, что хитрый хакер не поменял в токене роль USER на ADMIN.

Как это работает:

1. Клиент шлет Логин/Пароль -> Сервер проверяет и отдает JWT.

2. Клиент сохраняет JWT (обычно в LocalStorage браузера).

3. При каждом запросе клиент прикрепляет JWT в заголовок:
Authorization: Bearer <token>

4. Сервер видит токен, проверяет подпись и пускает (не ходя в базу данных!).

🛡 Настройка (Spring Boot 3.x)

Раньше мы наследовались от WebSecurityConfigurerAdapter. Забудьте, этот класс Deprecated.
Сейчас мы просто объявляем бин SecurityFilterChain.


@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable) // Для REST API отключаем
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // Никаких сессий!
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/**").permitAll() // Логин доступен всем
.requestMatchers("/admin/**").hasRole("ADMIN") // Админка только админам
.anyRequest().authenticated() // Всё остальное - только с токеном
)
// Добавляем наш кастомный фильтр для проверки JWT
.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

return http.build();
}
}



🔓 Что такое OAuth2?

JWT - это когда вы сами выдаете токены.
OAuth2 - это когда токены выдает кто-то большой и доверенный (Google, Facebook, GitHub).

Кнопка "Войти через Google" - это OAuth2.

1. Вы перенаправляете юзера на Google.

2. Google спрашивает: "Разрешить приложению MyShop узнать ваш email?".

3. Google возвращает вам токен.

4. Вы верите этому токену, потому что доверяете Google.

В Spring Boot это настраивается буквально в 5 строк в application.yaml, но под капотом там огромная машина стандартов.


🔥 Итог: безопасность для бэкенда это:

1. Spring Security как движок.

2. JWT как пропуск.

3. Stateless режим (без сессий).

4. HTTPS (обязательно, иначе токен украдут).

#SpringSecurity #JWT #OAuth2 #Java #CyberSecurity

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍92
А вы справитесь с тестом по HighLoad?

Как пройти путь от разработчика до архитектора высоконагруженных систем для работы с крупными проектами?

Пройдите тест, проверьте свои знания для обучения на курсе «Highload Architect» от OTUS. А так же и получите скидку 🎁 до 15.02.2026 - подробности у менеджера.

➡️ Пройти Тест https://vk.cc/cU6fLb

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

❗️Практическое обучение проводится в прямом эфире — вебинары не являются предзаписанными.

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
🏗 SOLID - Пять заповедей программиста

Почему один проект живет 10 лет и его легко дорабатывать, а другой через полгода превращается в "Legacy", к которому страшно подходить?
Разница в соблюдении принципов SOLID.

Это аббревиатура из 5 правил, сформулированных Робертом Мартином (Дядя Боб). Если вы нарушаете их - ваш код "гниет".

Давайте разберем каждую букву.

1️⃣ S - Single Responsibility Principle (Единственная ответственность)

"У класса должна быть только одна причина для изменения."


Как делают новички (God Object):
Класс OrderService делает всё:

1. Считает сумму заказа.
2. Сохраняет заказ в БД.
3. Отправляет email клиенту.
4. Генерирует PDF-чек.

Если бизнес попросит изменить формат чека — мы лезем в этот класс. Если поменяется логика БД - опять в него. Риск сломать отправку писем при правке базы данных огромен!

Как надо:
Разбиваем на маленькие классы:

OrderCalculator (считает).
OrderRepository (сохраняет).
EmailNotificationService (шлет письма).
PdfGenerator (печатает).

OrderService теперь просто дирижер, который вызывает эти компоненты.


2️⃣ O - Open-Closed Principle (Открытость/Закрытость)

"Программные сущности должны быть открыты для расширения, но закрыты для модификации."


Это значит: Не меняйте старый рабочий код, чтобы добавить новую фичу.

Плохо:
У нас есть метод расчета доставки.


if (deliveryType == "DHL") { ... }
else if (deliveryType == "Post") { ... }
// Пришла задача добавить FedEx? Придется лезть сюда и добавлять else if!



Хорошо:
Используем полиморфизм.


interface DeliveryStrategy { void deliver(); }
class DhlDelivery implements DeliveryStrategy { ... }
class PostDelivery implements DeliveryStrategy { ... }

// Нужен FedEx? Просто создаем НОВЫЙ класс, не трогая старые!
class FedExDelivery implements DeliveryStrategy { ... }




3️⃣ L - Liskov Substitution Principle (Принцип подстановки Барбары Лисков)

"Наследники должны без проблем заменять родителей."


Если у вас есть класс Bird с методом fly(), а вы создали наследника Penguin (Пингвин), который при вызове fly() бросает ошибку (потому что пингвины не летают) - вы нарушили LSP.

Суть: Если код работает с базовым классом, он должен работать и с любым его наследником, не зная об этом и не ломаясь.


4️⃣ I - Interface Segregation Principle (Разделение интерфейсов)

"Много маленьких интерфейсов лучше, чем один огромный."


Плохо:
Интерфейс Worker имеет методы work() и eat().
Мы создаем класс Robot. Роботы работают, но не едят.
Нам придется реализовать метод eat() и оставить его пустым или кинуть ошибку. Это мусор.

Хорошо:
Разбейте на Workable и Eatable.
Человек имплементирует оба. Робот - только Workable.


5️⃣ D - Dependency Inversion Principle (Инверсия зависимостей)

"Зависьте от абстракций, а не от конкретики."


Это то, что мы учили в Spring (DI).
Ваш Service не должен зависеть от PostgresRepository. Он должен зависеть от интерфейса Repository.
Тогда вы сможете легко подменить Postgres на MySQL или Mock-объект для тестов, не меняя ни строчки в Сервисе.


SOLID - это фильтр. Прежде чем закоммитить код, спросите себя:

1. Не делает ли мой класс слишком много? (S)
2. Придется ли мне переписывать этот класс, если добавятся новые условия? (O)
3. Не ломаю ли я поведение родителя? (L)
4. Не заставляю ли я других реализовывать ненужные методы? (I)
5. Завишу ли я от интерфейсов или от конкретных классов? (D)

#Architecture #SOLID #CleanCode #OODesign

📲 Мы в MAX

👉@BookJava
👍135🔥3
🏗 Порождающие паттерны: Как рождаются объекты?

Создать объект просто: User u = new User().
А если у объекта 20 полей? А если нам нужен только один экземпляр на всё приложение? А если мы не знаем заранее, какой именно класс нам нужен?
Тут на сцену выходят паттерны.

1️⃣ Singleton (Одиночка)

Суть: Гарантирует, что у класса есть только один экземпляр, и предоставляет к нему глобальную точку доступа.

Где нужен: Логгеры, Конфигурация, Пул соединений с БД.

Как реализовать:

1. Скрываем конструктор (private).
2. Создаем статическое поле с экземпляром.
3. Возвращаем его через статический метод.


public class Database {
// Единственный экземпляр
private static Database instance;

private Database() {} // Никто не создаст объект извне

public static synchronized Database getInstance() {
if (instance == null) {
instance = new Database();
}
return instance;
}
}



⚠️ Важно: В Spring Boot все бины по умолчанию - синглтоны. Вам не нужно писать этот код руками, контейнер Spring сам следит, чтобы сервис был создан один раз.


2️⃣ Builder (Строитель)

Суть: Позволяет создавать сложные объекты пошагово. Спасает от «Телескопического конструктора» (когда у вас конструктор с 10 аргументами, и вы не помните, где там age, а где height).

Было (Ужас):
new User("Alex", null, true, "admin", 25, null);

Стало (Builder):


User user = User.builder()
.name("Alex")
.age(25)
.role("ADMIN")
.active(true)
.build();



🛠 Лайфхак:
В Java не нужно писать Билдер руками (это 50 строк кода). Просто поставьте аннотацию Lombok @Builder над классом.


3️⃣ Factory Method (Фабричный метод)

Суть: Определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс инстанцировать.
Это реализация принципа Open-Closed. Мы добавляем новые типы продуктов, не ломая существующий код.

Пример: У нас есть "Уведомления". Сегодня это Email, завтра SMS, послезавтра Push.


// 1. Интерфейс
interface Notification { void send(String msg); }

// 2. Реализации
class EmailNotification implements Notification { ... }
class SmsNotification implements Notification { ... }

// 3. Фабрика (Решает, что создать)
class NotificationFactory {
public static Notification create(String type) {
return switch (type) {
case "EMAIL" -> new EmailNotification();
case "SMS" -> new SmsNotification();
default -> throw new IllegalArgumentException("Unknown type");
};
}
}

// Клиентский код (не знает про классы Email/Sms, знает только интерфейс)
Notification notification = NotificationFactory.create("SMS");
notification.send("Hello!");



🔥 Итог

1. Singleton - когда нужен один объект на всю систему.
2. Builder - когда объект сложный и у него много параметров.
3. Factory - когда мы не знаем заранее, какой конкретно объект понадобится, или хотим скрыть логику выбора.

#DesignPatterns #GoF #Singleton #Builder #Factory #Java

📲 Мы в MAX

👉@BookJava
👍5🔥3
🔴 Завтра тестовое собеседование с Java-разработчиком

11 февраля(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика.

Как это будет:
📂 Сергей Чамкин, старший разработчик из Uzum, ex-WildBerries, будет задавать реальные вопросы и задачи разработчику-добровольцу
📂 Cергей будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью
📂 В конце можно будет задать любой вопрос Сергею

Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.

Переходи в нашего бота, чтобы получить ссылку на эфир →
@shortcut_sh_bot

Реклама.
О рекламодателе.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍31
🏗 Структурные паттерны: Адаптер, Декоратор, Прокси

Когда вы пытаетесь соединить старую библиотеку с новым кодом или добавить логирование, не переписывая половину проекта - вы используете эти паттерны.

🔌 1. Adapter (Адаптер)

Суть: Делает несовместимые интерфейсы совместимыми.
Это как переходник для розетки. У вас вилка американская, а розетка европейская. Адаптер позволяет им работать вместе.

Где нужен: Когда есть старый класс (Legacy), который нельзя менять, но его нужно использовать в новом коде.

Пример: У нас есть система, которая понимает только KM/H (километры), а внешняя библиотека выдает скорость в MPH (мили).


// 1. Наш интерфейс (чего мы ждем)
interface Movable { double getSpeed(); } // км/ч

// 2. Чужой класс (что есть)
class Bugatti {
double getSpeedMph() { return 268; }
}

// 3. Адаптер (Переводчик)
class MovableAdapter implements Movable {
private Bugatti bugatti;

public MovableAdapter(Bugatti bugatti) {
this.bugatti = bugatti;
}

@Override
public double getSpeed() {
return convertMphToKmph(bugatti.getSpeedMph());
}
}




🎁 2. Decorator (Декоратор/Обертка)

Суть: Динамически добавляет объекту новые обязанности (функционал).
Это альтернатива наследованию. Вместо того чтобы создавать CoffeeWithMilkAndSugar, мы берем Coffee и заворачиваем его в Milk, а потом в Sugar.

Принцип: Матрешка. Каждый декоратор делает свою работу и вызывает следующий.

Пример:


// Базовый кофе
Coffee coffee = new SimpleCoffee();
System.out.println(coffee.getCost()); // 10$

// Добавили молоко (Обернули)
coffee = new MilkDecorator(coffee);
System.out.println(coffee.getCost()); // 12$

// Добавили сахар (Обернули еще раз)
coffee = new SugarDecorator(coffee);
System.out.println(coffee.getCost()); // 13$



Важно: В java.io это используется повсюду: new BufferedReader(new FileReader(file)).


🛡️ 3. Proxy (Заместитель)

Суть: Объект-прокладка, который контролирует доступ к другому объекту.
Клиент думает, что общается с реальным объектом, а на самом деле говорит с его заместителем.

Зачем?

1. Ленивая загрузка (Lazy Loading): Не грузить тяжелую картинку/БД, пока ее реально не попросят.

2. Безопасность: Проверить права доступа перед выполнением метода.

3. Логирование: Записать "Метод вызван" и передать вызов дальше.

Пример:


interface Image { void display(); }

class RealImage implements Image {
public RealImage(String file) { loadFromDisk(file); } // Долгая операция!
public void display() { System.out.println("Displaying..."); }
}

class ProxyImage implements Image {
private RealImage realImage;
private String file;

public ProxyImage(String file) { this.file = file; }

@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(file); // Грузим только сейчас!
}
realImage.display();
}
}



Spring Magic: Весь Spring держится на Прокси! Когда вы ставите @Transactional, Spring создает прокси вокруг вашего сервиса, открывает транзакцию, вызывает ваш метод, а потом закрывает транзакцию.


🔥 Итог

🔴Adapter - меняет интерфейс объекта (чтобы подошел).

🔴Decorator - меняет поведение объекта (добавляет фичи), не меняя интерфейс.

🔴Proxy - контролирует доступ к объекту (ленивость, защита).

#DesignPatterns #Adapter #Decorator #Proxy #Architecture

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
🎥 Открытый урок «Class Data Sharing и его перспективы».

🗓 17 февраля в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса
«Java Developer. Advanced».

Быстрый, лёгкий старт Java-сервисов — конкурентное преимущество. Разберём, чем поможет Class Data Sharing и где он уместен.


Что будет на вебинаре:
✔️ Назначение Class Data Sharing.
✔️ Поддержка и использование в Spring Boot.
✔️ Разница с Native Image для GraalVM.

В результате вебинара:
Сможете запустить Spring Boot-приложение с использованием CDS и понять базовую настройку. Получите представление, чем CDS отличается от Native Image (GraalVM).

Кому будет интересно:
Подойдёт Java-разработчикам и инженерам, которым важно ускорить старт сервисов и оптимизировать время запуска в проде.

🔗 Ссылка на регистрацию: https://vk.cc/cUiTYA

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
🧠 Поведенческие паттерны: Стратегия, Наблюдатель, Цепочка

Эти паттерны помогают избежать спагетти-кода, где один класс управляет всем миром через гигантские if-else.

🗺 1. Strategy (Стратегия)

Суть: Позволяет менять алгоритм поведения объекта прямо "на лету", во время выполнения программы.
Это убийца бесконечных if (type == "CARD") { ... } else if (type == "PAYPAL") { ... }.

Аналогия: Навигатор. Вы строите маршрут из точки А в точку Б. Стратегия - это способ передвижения:

🔴Пешком (один алгоритм).
🔴На машине (другой алгоритм).
🔴На автобусе (третий алгоритм).
Цель одна, пути реализации разные.

Код:


// Общий интерфейс
interface RouteStrategy {
void buildRoute(String a, String b);
}

// Конкретные стратегии
class RoadStrategy implements RouteStrategy { ... }
class WalkingStrategy implements RouteStrategy { ... }

// Контекст (Навигатор)
class Navigator {
private RouteStrategy strategy;

public void setStrategy(RouteStrategy strategy) {
this.strategy = strategy; // Меняем на лету!
}

public void buildRoute(String a, String b) {
strategy.buildRoute(a, b);
}
}




👀 2. Observer (Наблюдатель / Listener)

Суть: Один объект (Subject) меняет свое состояние, и все зависимые от него объекты (Observers) тут же узнают об этом.

Аналогия: YouTube-канал.

🔴Блогер (Subject) выпускает видео.
🔴Подписчики (Observers) получают уведомление.
🔴Если вы отписались - уведомления приходить перестанут.

Код: Это основа всех UI-фреймворков (кнопка нажата -> слушатель сработал) и даже архитектуры Kafka.


class NewsAgency {
private List<Channel> channels = new ArrayList<>();

public void subscribe(Channel channel) {
channels.add(channel);
}

public void broadcast(String news) {
for (Channel channel : channels) {
channel.update(news); // Уведомляем всех!
}
}
}




🔗 3. Chain of Responsibility (Цепочка обязанностей)

Суть: Запрос передается по цепочке обработчиков. Каждый обработчик решает: обработать запрос самому или передать следующему.

Аналогия: Техподдержка.

1. Сначала отвечает Чат-бот (Уровень 1). Не справился? -> Передает дальше.

2. Оператор колл-центра (Уровень 2). Не справился? -> Передает дальше.

3. Инженер (Уровень 3). Решает проблему.

Пример в Spring:
Spring Security работает именно так! Ваш HTTP-запрос проходит через цепочку фильтров:

🔴CorsFilter (проверяет домен) -> JwtFilter (проверяет токен) -> UsernamePasswordFilter (проверяет логин).
Если хоть один фильтр скажет "Нет", запрос дальше не пойдет.

Код:


abstract class SupportHandler {
protected SupportHandler next;

public void setNext(SupportHandler next) { this.next = next; }

public void handleRequest(String issue) {
if (canHandle(issue)) {
solve();
} else if (next != null) {
next.handleRequest(issue); // Передаем следующему
}
}
}




🔥 Итог

🔴Strategy - Выбираем алгоритм действия (Платим картой или кэшем?).
🔴Observer - Слушаем изменения (Вышло видео -> пришло уведомление).
🔴Chain of Responsibility - Передаем эстафету (Фильтр 1 -> Фильтр 2 -> Контроллер).

#DesignPatterns #Behavioral #Strategy #Observer #ChainOfResponsibility

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5