Библиотека собеса по Java | вопросы с собеседований
6.49K subscribers
412 photos
10 videos
647 links
Вопросы с собеседований по Java и ответы на них.

По рекламе: @proglib_adv

Учиться у нас: https://proglib.io/w/08c603b6

Для обратной связи: @proglibrary_feeedback_bot
Download Telegram
Расскажите о паттерне Bridge

Bridge — это структурный паттерн, который разделяет абстракцию и реализацию так, чтобы они могли изменяться независимо друг от друга.

Простыми словами: вместо одной толстой иерархии наследования вы разбиваете её на две независимые — «что делать» и «как делать» — и связываете их композицией.

▪️ Пример:

Система отправки уведомлений: типы уведомлений (срочное, обычное) × каналы доставки (email, SMS, push). Без Bridge это 6 классов, с каждым новым каналом — взрывной рост.

// Реализация — «как доставить»
interface MessageSender {
void send(String message, String recipient);
}

class EmailSender implements MessageSender {
public void send(String message, String recipient) {
System.out.println("Email → " + recipient + ": " + message);
}
}

class SmsSender implements MessageSender {
public void send(String message, String recipient) {
System.out.println("SMS → " + recipient + ": " + message);
}
}

// Абстракция — «что отправить»
abstract class Notification {
protected MessageSender sender; // мост к реализации

public Notification(MessageSender sender) {
this.sender = sender;
}

abstract void notify(String recipient, String message);
}

class UrgentNotification extends Notification {
public UrgentNotification(MessageSender sender) {
super(sender);
}

void notify(String recipient, String message) {
sender.send("[СРОЧНО] " + message, recipient);
sender.send("[СРОЧНО] Повторное напоминание: " + message, recipient);
}
}

class RegularNotification extends Notification {
public RegularNotification(MessageSender sender) {
super(sender);
}

void notify(String recipient, String message) {
sender.send(message, recipient);
}
}

// Использование — любая комбинация
Notification urgentSms = new UrgentNotification(new SmsSender());
urgentSms.notify("+79991234567", "Сервер упал");

Notification regularEmail = new RegularNotification(new EmailSender());
regularEmail.notify("dev@company.com", "Деплой завершён");


▪️ Когда использовать

Есть две ортогональные оси изменения (тип × реализация)
Хотите избежать «взрыва» подклассов
Нужно переключать реализацию в runtime

▪️ Bridge vs Strategy

— Strategy: меняет один алгоритм внутри объекта
— Bridge: разделяет целые иерархии абстракции и реализации

▪️ Минус

Усложняет код, если оси изменения всего одна — тогда достаточно обычного полиморфизма.

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
👍53🔥1
Расскажите о паттерне Composite

Composite — это структурный паттерн, который позволяет сгруппировать объекты в древовидную структуру и работать с ней так же, как с единичным объектом.

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

▪️ Пример:

Структура отдела в компании: сотрудник и команда состоят в одной иерархии. Нужно посчитать общую зарплату по любой ветке.

// Общий интерфейс
interface OrganizationUnit {
String getName();
long getSalary();
}

// Лист — отдельный сотрудник
class Employee implements OrganizationUnit {
private final String name;
private final long salary;

public Employee(String name, long salary) {
this.name = name;
this.salary = salary;
}

public String getName() { return name; }
public long getSalary() { return salary; }
}

// Композит — отдел, содержит другие элементы
class Department implements OrganizationUnit {
private final String name;
private final List<OrganizationUnit> units = new ArrayList<>();

public Department(String name) {
this.name = name;
}

public void add(OrganizationUnit unit) {
units.add(unit);
}

public String getName() { return name; }

public long getSalary() {
return units.stream()
.mapToLong(OrganizationUnit::getSalary)
.sum();
}
}

// Использование
Department dev = new Department("Разработка");
dev.add(new Employee("Анна", 200_000));
dev.add(new Employee("Борис", 180_000));

Department company = new Department("Компания");
company.add(dev);
company.add(new Employee("CEO Иван", 500_000));

System.out.println(company.getSalary()); // 880000


▪️ Когда использовать

Данные образуют древовидную структуру (файловая система, меню, оргструктура)
Клиентский код должен одинаково работать с простыми и составными объектами

▪️ Минус

Трудно ограничить типы компонентов внутри композита — приходится проверять в runtime.

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
👍51🔥1
Расскажите о паттерне Decorator

Decorator — это структурный паттерн, позволяющий на лету расширять поведение объекта, не изменяя его класс. Исходный объект помещается внутрь другого — декоратора, — который перехватывает вызовы и дополняет их нужной функциональностью.

Принцип тот же, что у матрёшки: каждый новый слой добавляет что-то своё.

▪️ Пример:

Сервис обработки данных: базовая загрузка, поверх — логирование, поверх — кэширование. Каждый слой добавляется независимо.

// Базовый интерфейс
interface DataService {
String getData(String key);
}

// Базовая реализация
class DatabaseService implements DataService {
public String getData(String key) {
return "data_from_db_" + key; // запрос в БД
}
}

// Абстрактный декоратор
abstract class DataServiceDecorator implements DataService {
protected final DataService delegate;

public DataServiceDecorator(DataService delegate) {
this.delegate = delegate;
}
}

// Декоратор логирования
class LoggingDecorator extends DataServiceDecorator {
public LoggingDecorator(DataService delegate) {
super(delegate);
}

public String getData(String key) {
System.out.println("LOG: запрос ключа " + key);
String result = delegate.getData(key);
System.out.println("LOG: получен результат");
return result;
}
}

// Декоратор кэширования
class CachingDecorator extends DataServiceDecorator {
private final Map<String, String> cache = new HashMap<>();

public CachingDecorator(DataService delegate) {
super(delegate);
}

public String getData(String key) {
return cache.computeIfAbsent(key, delegate::getData);
}
}

// Использование — собираем как конструктор
DataService service = new CachingDecorator(
new LoggingDecorator(
new DatabaseService()
)
);

service.getData("user:42"); // лог + БД + кэш
service.getData("user:42"); // кэш (без лога и БД)


▪️ Когда использовать

Нужно добавить поведение объекту без наследования
Комбинации поведений непредсказуемы (логирование + кэш + метрики в любом порядке)
Классический пример в JDK: InputStream → BufferedInputStream → GZIPInputStream

▪️ Decorator vs наследование

— Наследование: статическое, одно на класс
— Decorator: динамическое, можно комбинировать

▪️ Минус

Много мелких классов; отладка стека из нескольких обёрток может быть неудобной.

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍3🔥1
✔️ Java-тест: объект в HashSet есть, но contains() возвращает false

Добавили объект в Set. Он там. Но найти его невозможно. Утечка памяти в проде. Почему?

📦 Задание — code review

Команда хранит активные сессии в HashSet. После смены роли пользователя сессия «пропадает» из множества — contains() возвращает false, remove() не удаляет. Set растёт, память течёт.
public class UserSession {
private Long userId;
private String role;

public UserSession(Long userId, String role) {
this.userId = userId;
this.role = role;
}

// getters, setters

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserSession that = (UserSession) o;
return Objects.equals(userId, that.userId)
&& Objects.equals(role, that.role);
}

@Override
public int hashCode() {
return Objects.hash(userId, role);
}
}

@Service
public class SessionManager {

private final Set<UserSession> activeSessions = new HashSet<>();

public void addSession(UserSession session) {
activeSessions.add(session);
}

public void promoteToAdmin(Long userId) {
activeSessions.stream()
.filter(s -> s.getUserId().equals(userId))
.findFirst()
.ifPresent(s -> s.setRole("ADMIN"));
}

public boolean isActive(UserSession session) {
return activeSessions.contains(session);
}

public void removeSession(UserSession session) {
activeSessions.remove(session);
}
}


// Сценарий бага:
sessionManager.addSession(new UserSession(1L, "USER")); // ОК
sessionManager.promoteToAdmin(1L); // меняем роль
sessionManager.isActive(new UserSession(1L, "ADMIN")); // false ?!
// объект внутри Set есть, но достать его нельзя


▪️ Почему после promoteToAdmin() сессия становится невидимой для contains() и remove()?
▪️ Как исправить, не ломая бизнес-логику?

Ставьте → 🔥, если нравится формат. Если нет → 🌚

💬 Решения под спойлер. Сравним, какое будет лучше.

🐸 Библиотека собеса по Java

#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍31👏1
💬 Обратная связь

Как часто вы проходите собеседования?

🔥 — Сейчас активно ищу работу
👍🏼 — Раз в несколько месяцев
❤️ — Раз в полгода-год
😁 — Не прохожу, уже работаю/ещё учусь

🐸 Библиотека собеса по Java
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥25😁15👍32
Расскажите о паттерне Facade

Facade — это структурный паттерн, который предоставляет простой интерфейс к сложной подсистеме, скрывая внутреннюю сложность.

Простыми словами: один метод вместо десяти вызовов в разные сервисы. Пульт от телевизора — это фасад к электронике внутри.

▪️ Пример:

Оформление заказа: нужно проверить наличие, списать деньги, создать доставку, отправить уведомление. Клиент не должен знать эти детали.

// Сложная подсистема
class InventoryService {
boolean checkStock(String productId) {
return true; // проверка на складе
}
}

class PaymentService {
boolean charge(String userId, BigDecimal amount) {
return true; // списание средств
}
}

class ShippingService {
String createShipment(String orderId, String address) {
return "TRACK-" + orderId; // создание доставки
}
}

class NotificationService {
void sendOrderConfirmation(String userId, String trackingId) {
System.out.println("Уведомление отправлено: " + trackingId);
}
}

// Фасад — один метод вместо четырёх
class OrderFacade {
private final InventoryService inventory = new InventoryService();
private final PaymentService payment = new PaymentService();
private final ShippingService shipping = new ShippingService();
private final NotificationService notification = new NotificationService();

public String placeOrder(String userId, String productId,
BigDecimal amount, String address) {
if (!inventory.checkStock(productId)) {
throw new IllegalStateException("Нет на складе");
}
if (!payment.charge(userId, amount)) {
throw new IllegalStateException("Оплата не прошла");
}
String trackingId = shipping.createShipment(productId, address);
notification.sendOrderConfirmation(userId, trackingId);
return trackingId;
}
}

// Клиент вызывает один метод
OrderFacade facade = new OrderFacade();
String tracking = facade.placeOrder("user1", "SKU-100",
new BigDecimal("2999.00"), "Москва, ул. Пушкина");


▪️ Когда использовать

Подсистема сложная, а клиенту нужен простой вход
Хотите уменьшить связанность между слоями
Типичный пример: сервисный слой в Spring-приложении

▪️ Минус

Фасад может стать God Object, если взять на себя слишком много логики.

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥2🤔2
Расскажите о паттерне Proxy

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

Простыми словами: прокси выглядит как оригинал, но перед вызовом может проверить права, закэшировать результат или создать объект лениво.

▪️ Пример:

Сервис загрузки документов: реальная загрузка — тяжёлая операция. Прокси проверяет права доступа и кэширует результат.

interface DocumentService {
String loadDocument(String docId);
}

// Реальный сервис — тяжёлая операция
class RealDocumentService implements DocumentService {
public String loadDocument(String docId) {
System.out.println("Загрузка из хранилища: " + docId);
return "Содержимое документа " + docId;
}
}

// Прокси — контроль доступа + кэширование
class DocumentServiceProxy implements DocumentService {
private final RealDocumentService realService = new RealDocumentService();
private final Map<String, String> cache = new HashMap<>();
private final Set<String> allowedRoles;

public DocumentServiceProxy(Set<String> allowedRoles) {
this.allowedRoles = allowedRoles;
}

public String loadDocument(String docId) {
// 1. Проверка доступа
String currentRole = SecurityContext.getCurrentRole();
if (!allowedRoles.contains(currentRole)) {
throw new SecurityException("Нет доступа");
}

// 2. Кэширование
return cache.computeIfAbsent(docId, realService::loadDocument);
}
}

// Клиент работает с прокси как с оригиналом
DocumentService service = new DocumentServiceProxy(Set.of("ADMIN", "EDITOR"));
String doc = service.loadDocument("report-2026");


▪️ Виды прокси

Protection Proxy — контроль доступа (Spring Security)
Caching Proxy — кэширование результатов
Lazy Proxy — отложенная инициализация (Hibernate lazy loading)
Remote Proxy — доступ к удалённому объекту (RMI)

▪️ Proxy vs Decorator

— Proxy: контролирует жизненный цикл и доступ к объекту
— Decorator: добавляет поведение, не контролируя доступ

▪️ Минус

Увеличивает задержку отклика; усложняет отладку при нескольких слоях проксирования.

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥1
Расскажите о паттерне Observer

Observer — это поведенческий паттерн, который создаёт механизм подписки, позволяя одним объектам следить за изменениями в других.

Простыми словами: как подписка на Telegram-канал — когда выходит новый пост, все подписчики получают уведомление автоматически.

▪️ Пример:

Сервис обработки заказов: при смене статуса нужно уведомить склад, аналитику и клиента. Вместо жёстких вызовов — подписка.

// Событие
record OrderEvent(String orderId, String status) {}

// Подписчик
interface OrderListener {
void onOrderStatusChanged(OrderEvent event);
}

// Издатель
class OrderService {
private final List<OrderListener> listeners = new ArrayList<>();

public void subscribe(OrderListener listener) {
listeners.add(listener);
}

public void changeStatus(String orderId, String newStatus) {
// бизнес-логика...
OrderEvent event = new OrderEvent(orderId, newStatus);
listeners.forEach(l -> l.onOrderStatusChanged(event));
}
}

// Конкретные подписчики
class WarehouseListener implements OrderListener {
public void onOrderStatusChanged(OrderEvent event) {
if ("PAID".equals(event.status())) {
System.out.println("Склад: начать сборку " + event.orderId());
}
}
}

class AnalyticsListener implements OrderListener {
public void onOrderStatusChanged(OrderEvent event) {
System.out.println("Аналитика: статус " + event.status());
}
}

// Использование
OrderService service = new OrderService();
service.subscribe(new WarehouseListener());
service.subscribe(new AnalyticsListener());

service.changeStatus("ORD-42", "PAID");
// Склад: начать сборку ORD-42
// Аналитика: статус PAID


▪️ Когда использовать

Изменение одного объекта требует обновления других, и набор зависимых объектов заранее неизвестен
Хотите избежать жёсткой связи между компонентами
В Spring: ApplicationEventPublisher — встроенный Observer

▪️ Минус

Подписчики оповещаются в непредсказуемом порядке; утечки памяти, если забыть отписаться.

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍2🤔1
Расскажите о паттерне Command

Command — это поведенческий паттерн, который превращает запрос в отдельный объект, содержащий всю информацию о запросе.

Простыми словами: вместо прямого вызова метода вы создаёте объект-команду, которую можно передать, поставить в очередь, отменить или повторить.

▪️ Пример:

Текстовый редактор с поддержкой undo/redo: каждое действие — объект, который можно откатить.

// Команда
interface Command {
void execute();
void undo();
}

// Получатель
class TextEditor {
private StringBuilder text = new StringBuilder();

public void insert(int pos, String str) {
text.insert(pos, str);
}

public void delete(int pos, int length) {
text.delete(pos, pos + length);
}

public String getText() { return text.toString(); }
}

// Конкретная команда
class InsertCommand implements Command {
private final TextEditor editor;
private final int position;
private final String text;

public InsertCommand(TextEditor editor, int position, String text) {
this.editor = editor;
this.position = position;
this.text = text;
}

public void execute() {
editor.insert(position, text);
}

public void undo() {
editor.delete(position, text.length());
}
}

// Инвокер с историей
class CommandHistory {
private final Deque<Command> history = new ArrayDeque<>();

public void execute(Command cmd) {
cmd.execute();
history.push(cmd);
}

public void undo() {
if (!history.isEmpty()) {
history.pop().undo();
}
}
}

// Использование
TextEditor editor = new TextEditor();
CommandHistory history = new CommandHistory();

history.execute(new InsertCommand(editor, 0, "Hello"));
history.execute(new InsertCommand(editor, 5, " World"));
System.out.println(editor.getText()); // Hello World

history.undo();
System.out.println(editor.getText()); // Hello


▪️ Когда использовать

Нужен undo/redo
Команды нужно ставить в очередь, логировать или выполнять отложенно
Хотите отделить объект, инициирующий операцию, от объекта, выполняющего её

▪️ Минус

Усложняет код: каждая операция — отдельный класс.

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍3🔥1
Расскажите о паттерне Template Method

Template Method — это поведенческий паттерн, который определяет скелет алгоритма в суперклассе, а подклассы переопределяют конкретные шаги, не меняя общую структуру.

Простыми словами: рецепт борща одинаков (порезать → сварить → подать), но ингредиенты у каждого свои.

▪️ Пример:

Генератор отчётов: шаги одинаковые (загрузить данные → обработать → отформатировать → отправить), но формат вывода — PDF, Excel, HTML — разный.

// Абстрактный класс с шаблонным методом
abstract class ReportGenerator {

// Template Method — final, чтобы подклассы не меняли порядок
public final void generateReport() {
List<Map<String, Object>> data = fetchData();
List<Map<String, Object>> processed = processData(data);
String output = format(processed);
send(output);
}

// Общий шаг
private List<Map<String, Object>> fetchData() {
System.out.println("Загрузка из БД...");
return List.of(Map.of("revenue", 1_000_000));
}

// Общий шаг с возможностью расширения
protected List<Map<String, Object>> processData(
List<Map<String, Object>> data) {
return data; // по умолчанию — без обработки
}

// Абстрактный шаг — подклассы обязаны реализовать
protected abstract String format(List<Map<String, Object>> data);

// Hook — подклассы могут переопределить
protected void send(String output) {
System.out.println("Отправка по email: " + output.substring(0, 50));
}
}

class PdfReportGenerator extends ReportGenerator {
protected String format(List<Map<String, Object>> data) {
return "PDF: " + data.toString();
}
}

class ExcelReportGenerator extends ReportGenerator {
protected String format(List<Map<String, Object>> data) {
return "XLSX: " + data.toString();
}

@Override
protected void send(String output) {
System.out.println("Загрузка в Google Drive: " + output.substring(0, 50));
}
}

// Использование
new PdfReportGenerator().generateReport();
new ExcelReportGenerator().generateReport();


▪️ Когда использовать

Алгоритм одинаков по структуре, но шаги различаются
Хотите избежать дублирования кода между похожими классами
В Spring: JdbcTemplate, RestTemplate — классические примеры

▪️ Template Method vs Strategy

— Template Method: наследование, подклассы переопределяют шаги
— Strategy: композиция, алгоритм передаётся извне

▪️ Минус

Жёсткая привязка к наследованию; нарушается LSP, если подклассы ломают контракт шагов.

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥2👏2
Расскажите о паттерне State

State — это поведенческий паттерн, который позволяет объекту менять своё поведение при изменении внутреннего состояния. Извне кажется, что объект сменил свой класс.

Простыми словами: банкомат ведёт себя по-разному в зависимости от состояния: ожидание карты → ввод PIN → выбор операции. Одна и та же кнопка делает разные вещи.

▪️ Пример:

Заказ в интернет-магазине проходит через состояния: Новый → Оплачен → Доставляется → Завершён. В каждом состоянии допустимы разные действия.

// Интерфейс состояния
interface OrderState {
void pay(OrderContext order);
void ship(OrderContext order);
void deliver(OrderContext order);
}

// Контекст
class OrderContext {
private OrderState state;

public OrderContext() {
this.state = new NewOrderState();
}

public void setState(OrderState state) {
this.state = state;
System.out.println("Статус → " + state.getClass().getSimpleName());
}

public void pay() { state.pay(this); }
public void ship() { state.ship(this); }
public void deliver() { state.deliver(this); }
}

class NewOrderState implements OrderState {
public void pay(OrderContext order) {
System.out.println("Оплата принята");
order.setState(new PaidOrderState());
}
public void ship(OrderContext order) {
System.out.println("Нельзя отправить неоплаченный заказ");
}
public void deliver(OrderContext order) {
System.out.println("Заказ ещё не отправлен");
}
}

class PaidOrderState implements OrderState {
public void pay(OrderContext order) {
System.out.println("Уже оплачен");
}
public void ship(OrderContext order) {
System.out.println("Заказ передан курьеру");
order.setState(new ShippedOrderState());
}
public void deliver(OrderContext order) {
System.out.println("Заказ ещё не отправлен");
}
}

class ShippedOrderState implements OrderState {
public void pay(OrderContext order) {
System.out.println("Уже оплачен");
}
public void ship(OrderContext order) {
System.out.println("Уже в пути");
}
public void deliver(OrderContext order) {
System.out.println("Заказ доставлен!");
order.setState(new DeliveredOrderState());
}
}

class DeliveredOrderState implements OrderState {
public void pay(OrderContext o) { System.out.println("Завершён"); }
public void ship(OrderContext o) { System.out.println("Завершён"); }
public void deliver(OrderContext o) { System.out.println("Уже доставлен"); }
}

// Использование
OrderContext order = new OrderContext();
order.pay(); // Оплата принята → PaidOrderState
order.ship(); // Передан курьеру → ShippedOrderState
order.deliver(); // Доставлен! → DeliveredOrderState
order.pay(); // Завершён


▪️ Когда использовать

Объект ведёт себя по-разному в зависимости от состояния
Много if/switch по статусу — признак, что нужен State
Количество состояний может расти

▪️ State vs Strategy

— Strategy: клиент выбирает алгоритм
— State: объект сам переключает поведение при смене состояния

▪️ Минус

Избыточен, если состояний всего 2–3 и переходы простые.

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
👍41🔥1
💬 Обратная связь

Последние посты все по единой теме паттернов.
Удобно ли, если посты будут иногда такими едиными блоками?

🔥 — Удобно
👍🏼 — Без разницы
😁 — Скука смертная, хочется разнообразия

🐸 Библиотека собеса по Java
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥25😁6👍3
Расскажите о паттерне Chain of Responsibility

Chain of Responsibility — это поведенческий паттерн, который позволяет передавать запрос по цепочке обработчиков. Каждый обработчик решает: обработать запрос или передать дальше.

Простыми словами: как эскалация тикета: L1 → L2 → L3 саппорт. Каждый уровень либо решает проблему, либо передаёт выше.

▪️ Пример:

Валидация HTTP-запроса: проверка аутентификации → авторизации → rate limit → бизнес-логика. Каждый шаг — звено цепочки.

// Абстрактный обработчик
abstract class RequestHandler {
private RequestHandler next;

public RequestHandler setNext(RequestHandler next) {
this.next = next;
return next; // для цепочки вызовов
}

public void handle(HttpRequest request) {
if (next != null) {
next.handle(request);
}
}
}

class AuthenticationHandler extends RequestHandler {
public void handle(HttpRequest request) {
if (request.getToken() == null) {
throw new SecurityException("Нет токена");
}
System.out.println("✓ Аутентификация пройдена");
super.handle(request); // передаём дальше
}
}

class RateLimitHandler extends RequestHandler {
private final Map<String, Integer> counters = new ConcurrentHashMap<>();

public void handle(HttpRequest request) {
int count = counters.merge(request.getIp(), 1, Integer::sum);
if (count > 100) {
throw new RuntimeException("429 Too Many Requests");
}
System.out.println("✓ Rate limit OK");
super.handle(request);
}
}

class BusinessLogicHandler extends RequestHandler {
public void handle(HttpRequest request) {
System.out.println("✓ Бизнес-логика выполнена");
// конец цепочки
}
}

// Сборка цепочки
RequestHandler chain = new AuthenticationHandler();
chain.setNext(new RateLimitHandler())
.setNext(new BusinessLogicHandler());

chain.handle(request);


▪️ Когда использовать

Набор обработчиков и их порядок определяется динамически
Запрос должен быть обработан одним из нескольких объектов, но каким — неизвестно заранее
Классический пример: фильтры в Servlet API (javax.servlet.Filter)

▪️ Минус

Запрос может пройти всю цепочку и не быть обработанным; сложно отладить длинную цепочку.

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥21👍1
Расскажите о паттерне Iterator

Iterator — это поведенческий паттерн, который даёт возможность последовательно обходить элементы коллекции, не раскрывая её внутреннее устройство.

Простыми словами: вы проходите по элементам через «окошко» (hasNext / next), не зная — это массив, дерево или база данных за ним.

▪️ Пример:

Пагинация по результатам из БД: клиент обходит элементы как обычную коллекцию, а итератор незаметно подгружает следующую страницу.

class PaginatedIterator<T> implements Iterator<T> {
private final Function<Integer, List<T>> pageFetcher;
private final int pageSize;
private List<T> currentPage;
private int pageIndex = 0;
private int itemIndex = 0;

public PaginatedIterator(Function<Integer, List<T>> pageFetcher,
int pageSize) {
this.pageFetcher = pageFetcher;
this.pageSize = pageSize;
this.currentPage = pageFetcher.apply(0);
}

public boolean hasNext() {
if (itemIndex < currentPage.size()) return true;
if (currentPage.size() < pageSize) return false; // последняя страница

// Подгружаем следующую страницу
pageIndex++;
currentPage = pageFetcher.apply(pageIndex);
itemIndex = 0;
return !currentPage.isEmpty();
}

public T next() {
if (!hasNext()) throw new NoSuchElementException();
return currentPage.get(itemIndex++);
}
}

// Использование — клиент не знает про пагинацию
Iterator<User> users = new PaginatedIterator<>(
page -> userRepository.findAll(PageRequest.of(page, 50)).getContent(),
50
);

while (users.hasNext()) {
User user = users.next(); // подгрузка происходит автоматически
process(user);
}


▪️ Когда использовать

Нужен единый способ обхода для разных структур данных
Хотите скрыть сложность обхода (дерево, граф, пагинация)
В Java: все коллекции реализуют Iterable, что позволяет for-each

▪️ Минус

Для простых коллекций — избыточен; итератор может устареть при изменении коллекции (ConcurrentModificationException).

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
👍31🔥1
Расскажите о паттерне Mediator

Mediator — это поведенческий паттерн, который убирает прямые связи между компонентами, заставляя их общаться через посредника.

Простыми словами: диспетчер в аэропорту — самолёты не переговариваются друг с другом, а общаются через башню управления.

▪️ Пример:

Чат-комната: пользователи не отправляют сообщения друг другу напрямую — всё идёт через комнату, которая решает, кому доставить.

// Медиатор
interface ChatRoom {
void sendMessage(String message, User sender);
void addUser(User user);
}

class GroupChatRoom implements ChatRoom {
private final List<User> users = new ArrayList<>();

public void addUser(User user) {
users.add(user);
user.setChatRoom(this);
}

public void sendMessage(String message, User sender) {
users.stream()
.filter(u -> u != sender)
.forEach(u -> u.receive(message, sender.getName()));
}
}

// Коллега
class User {
private final String name;
private ChatRoom chatRoom;

public User(String name) {
this.name = name;
}

public void setChatRoom(ChatRoom room) {
this.chatRoom = room;
}

public void send(String message) {
chatRoom.sendMessage(message, this);
}

public void receive(String message, String from) {
System.out.println(name + " получил от " + from + ": " + message);
}

public String getName() { return name; }
}

// Использование
ChatRoom room = new GroupChatRoom();
User alice = new User("Alice");
User bob = new User("Bob");
User carol = new User("Carol");

room.addUser(alice);
room.addUser(bob);
room.addUser(carol);

alice.send("Привет всем!");
// Bob получил от Alice: Привет всем!
// Carol получил от Alice: Привет всем!


▪️ Когда использовать

Компоненты слишком сильно связаны друг с другом
Хотите переиспользовать компоненты в других контекстах
Примеры: Spring MVC DispatcherServlet — медиатор между контроллерами

▪️ Минус

Медиатор может стать God Object, сконцентрировав слишком много логики.

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
👍31🔥1👏1
This media is not supported in your browser
VIEW IN TELEGRAM
Перед вами классическая визуализация одного из методов сортировки.

как считаете, какой это метод?
👍— Сортировка выбором (Selection Sort)
❤️— Сортировка слиянием (Merge Sort)
🔥— Сортировка пузырьком (Bubble Sort)

🔹 Курс «Алгоритмы и структуры данных»
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib

🏃‍♀️ Азбука айтишника

#магиякода
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥117👍3
Расскажите о паттерне Memento

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

Простыми словами: Ctrl+Z в любом редакторе — где-то хранится снимок предыдущего состояния, к которому можно откатиться.

▪️ Пример:

Редактор конфигурации сервера: администратор меняет настройки и может откатиться к любой предыдущей версии.

// Memento — снимок состояния (immutable)
record ConfigMemento(
Map<String, String> properties,
LocalDateTime timestamp
) {
public ConfigMemento {
properties = Map.copyOf(properties); // защитная копия
}
}

// Originator — объект, чьё состояние сохраняем
class ServerConfig {
private final Map<String, String> properties = new HashMap<>();

public void set(String key, String value) {
properties.put(key, value);
}

public String get(String key) {
return properties.get(key);
}

public ConfigMemento save() {
return new ConfigMemento(properties, LocalDateTime.now());
}

public void restore(ConfigMemento memento) {
properties.clear();
properties.putAll(memento.properties());
}
}

// Caretaker — хранит историю снимков
class ConfigHistory {
private final Deque<ConfigMemento> snapshots = new ArrayDeque<>();

public void backup(ServerConfig config) {
snapshots.push(config.save());
}

public void undo(ServerConfig config) {
if (!snapshots.isEmpty()) {
config.restore(snapshots.pop());
}
}
}

// Использование
ServerConfig config = new ServerConfig();
ConfigHistory history = new ConfigHistory();

config.set("maxThreads", "200");
history.backup(config);

config.set("maxThreads", "500");
config.set("timeout", "30s");
history.backup(config);

config.set("maxThreads", "1"); // ой, ошибка
history.undo(config);

System.out.println(config.get("maxThreads")); // 500


▪️ Когда использовать

Нужна функция отмены/отката (undo)
Нужно сохранять контрольные точки состояния
Прямой доступ к полям объекта нарушил бы инкапсуляцию

▪️ Memento vs Command

— Command: хранит действие и умеет его отменить
— Memento: хранит полный снимок состояния

▪️ Минус

Может потреблять много памяти, если состояние объекта большое и снимки создаются часто.

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍2🔥1
До 31 мая можно забрать любой курс Proglib Academy со скидкой 40%

Если давно хотели прокачаться в Python, ML, алгоритмах или AI-агентах, сейчас самое время выбрать программу и начать обучение по сниженной цене.

🎁 Разработка AI-агентов от 49.000 ₽ (вместо 69.000 ₽)

Практический курс по разработке AI-агентов для автоматизации задач, работы и собственных проектов

🎁 Курс AgentOps129.000 ₽ (вместо 149.000 ₽)

Для разработчиков и LLM-инженеров, которые хотят внедрять AI-логику в бэкенд и сохранять стабильность сервиса.

🎁 Математика для разработки AI-моделей 23.990 ₽ (вместо 31.990 ₽)

Практическая база по математике для анализа данных, ML и дальнейшего развития в AI.

🎁 Математика для Data Scienceот 29.990 ₽ (вместо 39.990 ₽)

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

🎁 ML для старта в Data Science28.990 ₽ (вместо 38.990 ₽)

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

🎁 Основы IT для непрограммистов16.990 ₽ (вместо 28.990 ₽)

Курс для IT-рекрутеров, маркетологов, проджектов, продактов и всех, кто работает с IT, но не пишет код.

🎁 Архитектуры и шаблоны проектирования27.990 ₽ (вместо 37.900 ₽)

Освоите основные паттерны проектирования и прокачаете навыки архитектора программного обеспечения.

🎁 Специалист по ИИ89.000 ₽ (вместо 113.900 ₽)

Курс для тех, кто хочет получить профессию в сфере ИИ, собрать портфолио из 5 проектов и научиться разрабатывать сложных AI-агентов.

🎁 Алгоритмы и структуры данных 33.990 ₽ (вместо 57.990 ₽)

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

🎁 Программирование на языке Python27.990 ₽ (вместо 47.390 ₽)

Освоите Python на практике: без сухой теории, с пошаговой прокачкой навыков и итоговым проектом в портфолио.

🙌 Выбирайте курс по ссылке, оставляйте заявку, и менеджер поможет подобрать программу под ваши цели — https://clc.to/SALE40
Расскажите о паттерне Visitor

Visitor — это поведенческий паттерн, который позволяет добавлять новые операции к объектам, не изменяя их классы.

Простыми словами: налоговый инспектор (visitor) приходит в разные компании и выполняет проверку — компании не меняются, а новые виды проверок добавляются легко.

▪️ Пример:

Система документооборота: есть разные типы документов, и нужно добавлять операции (экспорт, валидация, подсчёт статистики) без изменения классов документов.

// Visitor
interface DocumentVisitor {
void visit(Invoice invoice);
void visit(Contract contract);
void visit(Report report);
}

// Элементы
interface Document {
void accept(DocumentVisitor visitor);
}

class Invoice implements Document {
private final BigDecimal amount;
public Invoice(BigDecimal amount) { this.amount = amount; }
public BigDecimal getAmount() { return amount; }

public void accept(DocumentVisitor visitor) {
visitor.visit(this); // double dispatch
}
}

class Contract implements Document {
private final LocalDate expiryDate;
public Contract(LocalDate expiryDate) { this.expiryDate = expiryDate; }
public LocalDate getExpiryDate() { return expiryDate; }

public void accept(DocumentVisitor visitor) {
visitor.visit(this);
}
}

class Report implements Document {
private final int pageCount;
public Report(int pageCount) { this.pageCount = pageCount; }
public int getPageCount() { return pageCount; }

public void accept(DocumentVisitor visitor) {
visitor.visit(this);
}
}

// Конкретный visitor — новая операция без изменения документов
class ExportVisitor implements DocumentVisitor {
public void visit(Invoice invoice) {
System.out.println("Экспорт счёта: " + invoice.getAmount() + " ₽");
}
public void visit(Contract contract) {
System.out.println("Экспорт договора до " + contract.getExpiryDate());
}
public void visit(Report report) {
System.out.println("Экспорт отчёта: " + report.getPageCount() + " стр.");
}
}

// Использование
List<Document> docs = List.of(
new Invoice(new BigDecimal("150000")),
new Contract(LocalDate.of(2027, 1, 1)),
new Report(42)
);

DocumentVisitor exporter = new ExportVisitor();
docs.forEach(doc -> doc.accept(exporter));


▪️ Когда использовать

Нужно выполнить операцию над группой разнородных объектов
Новые операции добавляются часто, а новые типы элементов — редко
Пример из JDK: java.nio.file.FileVisitor

▪️ Минус

Visitor нужно обновлять при добавлении нового типа элемента — нарушается Open/Closed Principle для элементов.

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1👏1
Расскажите о паттерне Interpreter

Interpreter — это поведенческий паттерн, который определяет грамматику простого языка и интерпретатор для его предложений.

Простыми словами: вы описываете правила «мини-языка» в виде классов, и каждый класс умеет вычислить свою часть выражения. Как калькулятор, который разбирает «3 + 5 * 2».

▪️ Пример:

Движок фильтрации пользователей по правилам: «возраст > 18 AND город = Москва». Каждое условие — узел дерева выражений.

// Абстрактное выражение
interface Expression {
boolean interpret(Map<String, String> context);
}

// Терминальное выражение — проверка одного поля
class Equals implements Expression {
private final String key;
private final String value;

public Equals(String key, String value) {
this.key = key;
this.value = value;
}

public boolean interpret(Map<String, String> context) {
return value.equals(context.get(key));
}
}

class GreaterThan implements Expression {
private final String key;
private final int threshold;

public GreaterThan(String key, int threshold) {
this.key = key;
this.threshold = threshold;
}

public boolean interpret(Map<String, String> context) {
return Integer.parseInt(context.getOrDefault(key, "0")) > threshold;
}
}

// Нетерминальные выражения — комбинаторы
class And implements Expression {
private final Expression left, right;

public And(Expression left, Expression right) {
this.left = left;
this.right = right;
}

public boolean interpret(Map<String, String> context) {
return left.interpret(context) && right.interpret(context);
}
}

class Or implements Expression {
private final Expression left, right;

public Or(Expression left, Expression right) {
this.left = left;
this.right = right;
}

public boolean interpret(Map<String, String> context) {
return left.interpret(context) || right.interpret(context);
}
}

// Использование: возраст > 18 AND город = Москва
Expression rule = new And(
new GreaterThan("age", 18),
new Equals("city", "Москва")
);

Map<String, String> user1 = Map.of("age", "25", "city", "Москва");
Map<String, String> user2 = Map.of("age", "16", "city", "Москва");

System.out.println(rule.interpret(user1)); // true
System.out.println(rule.interpret(user2)); // false


▪️ Когда использовать

Есть простой язык или набор правил, которые нужно интерпретировать
Грамматика стабильна, но выражений — много
Пример: SpEL (Spring Expression Language), регулярные выражения

▪️ Минус

Для сложных грамматик дерево классов разрастается и становится неуправляемым — лучше использовать парсер-генераторы (ANTLR).

🐸 Библиотека собеса по Java

#patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
💬 Обратная связь

Текущий уровень сложности вопросов?

🔥 — Слишком просто, хочу сложнее
👍🏼 — В самый раз
❤️ — Иногда сложновато
😁 — Часто не понимаю

🐸 Библиотека собеса по Java
Please open Telegram to view this post
VIEW IN TELEGRAM
👍123🔥2😁2