Библиотека 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
🏗 Порождающие паттерны: Как рождаются объекты?

Создать объект просто: 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
🏗 Структурные паттерны: Адаптер, Декоратор, Прокси

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

🔌 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
🧠 Поведенческие паттерны: Стратегия, Наблюдатель, Цепочка

Эти паттерны помогают избежать спагетти-кода, где один класс управляет всем миром через гигантские 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