es
Feedback

¡No caigas en manos de tramposos! Telemetrio encuentra y marca estos canales 👉 Si quieres ver la etiqueta, suscríbete

Библиотека собеса по Java | вопросы с собеседований

Библиотека собеса по Java | вопросы с собеседований

Ir al canal en Telegram

Вопросы с собеседований по Java и ответы на них. По рекламе: @tproger_sales_bot Учиться у нас: clc.to/3wECtA Для обратной связи: @proglibrary_feeedback_bot

Mostrar más
6 378
Suscriptores
-624 horas
-197 días
-5130 días
Archivo de publicaciones
До 31 мая можно забрать любой курс Proglib Academy со скидкой 40% Если давно хотели прокачаться в Python, ML, алгоритмах или
До 31 мая можно забрать любой курс Proglib Academy со скидкой 40% Если давно хотели прокачаться в Python, ML, алгоритмах или AI-агентах, сейчас самое время выбрать программу и начать обучение по сниженной цене. 🎁 Разработка AI-агентов — от 49.000 ₽ (вместо 69.000 ₽) Практический курс по разработке AI-агентов для автоматизации задач, работы и собственных проектов 🎁 Курс AgentOps — 129.000 ₽ (вместо 149.000 ₽) Для разработчиков и LLM-инженеров, которые хотят внедрять AI-логику в бэкенд и сохранять стабильность сервиса. 🎁 Математика для разработки AI-моделей — 23.990 ₽ (вместо 31.990 ₽) Практическая база по математике для анализа данных, ML и дальнейшего развития в AI. 🎁 Математика для Data Science — от 29.990 ₽ (вместо 39.990 ₽) Курс для тех, кто хочет решать задачи, которые дают на собеседованиях на позицию дата-сайентиста в бигтехе. 🎁 ML для старта в Data Science — 28.990 ₽ (вместо 38.990 ₽) Разберётесь в машинном обучении: от базовых понятий и линейных моделей до ансамблей, бустинга и рекомендательных систем. 🎁 Основы IT для непрограммистов — 16.990 ₽ (вместо 28.990 ₽) Курс для IT-рекрутеров, маркетологов, проджектов, продактов и всех, кто работает с IT, но не пишет код. 🎁 Архитектуры и шаблоны проектирования — 27.990 ₽ (вместо 37.900 ₽) Освоите основные паттерны проектирования и прокачаете навыки архитектора программного обеспечения. 🎁 Специалист по ИИ — 89.000 ₽ (вместо 113.900 ₽) Курс для тех, кто хочет получить профессию в сфере ИИ, собрать портфолио из 5 проектов и научиться разрабатывать сложных AI-агентов. 🎁 Алгоритмы и структуры данных — 33.990 ₽ (вместо 57.990 ₽) Подготовитесь к алгоритмическим собеседованиям, разберёте структуры данных и научитесь писать более эффективный код. 🎁 Программирование на языке Python — 27.990 ₽ (вместо 47.390 ₽) Освоите Python на практике: без сухой теории, с пошаговой прокачкой навыков и итоговым проектом в портфолио. 🙌 Выбирайте курс по ссылке, оставляйте заявку, и менеджер поможет подобрать программу под ваши цели — https://clc.to/SALE40

❓ Расскажите о паттерне 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

Перед вами классическая визуализация одного из методов сортировки. как считаете, какой это метод? 👍— Сортировка выбором (Selection Sort) ❤️— Сортировка слиянием (Merge Sort) 🔥— Сортировка пузырьком (Bubble Sort) 🔹 Курс «Алгоритмы и структуры данных» 🔹 Получить консультацию менеджера 🔹 Сайт Академии 🔹 Сайт Proglib 🏃‍♀️ Азбука айтишника #магиякода

❓ Расскажите о паттерне 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

🔴 Завтра тестовое собеседование с Java-разработчиком 27 мая(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседов
🔴 Завтра тестовое собеседование с Java-разработчиком 27 мая(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика. Как это будет: 📂 Сергей Чамкин, старший разработчик из Uzum, ex-WildBerries, будет задавать реальные вопросы и задачи разработчику-добровольцу 📂 Cергей будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью 📂 В конце можно будет задать любой вопрос Сергею Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы. Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_sh_bot Реклама. О рекламодателе.

❓ Расскажите о паттерне 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

❓ Расскажите о паттерне 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

💬 Обратная связь Последние посты все по единой теме паттернов. Удобно ли, если посты будут иногда такими едиными блоками? 🔥 — Удобно 👍🏼 — Без разницы 😁 — Скука смертная, хочется разнообразия 🐸 Библиотека собеса по Java

❓ Расскажите о паттерне 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

❓ Расскажите о паттерне 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

❓ Расскажите о паттерне 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

❓ Расскажите о паттерне 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

❓ Расскажите о паттерне 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

❓ Расскажите о паттерне 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

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

✔️ 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

❓ Расскажите о паттерне 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

❓ Расскажите о паттерне 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

❓ Расскажите о паттерне 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

🔥 Знакомьтесь с экспертом Proglib.academy: Эмиль Сатаев Эмиль — эксперт с 8-летним опытом в разработке, который специализиру
🔥 Знакомьтесь с экспертом Proglib.academy: Эмиль Сатаев Эмиль — эксперт с 8-летним опытом в разработке, который специализируется на внедрении LLM и агентных подходов в реальные коммерческие сервисы. Он точно знает, как проектировать архитектуру так, чтобы ИИ-функции работали стабильно в связке с внешними системами. 🏃‍♀️ Уже 14 мая Эмиль проведет открытый вебинар! Обсудим самую «больную» тему: «Почему AI-продукты на базе LLM ломаются и как сделать, чтобы работало». 🗓 Когда: 14 мая в 19:00 (Мск) Почему Эмиля стоит послушать: 🟣 8+ лет в разработке (Backend и Frontend)
Прошел путь от фулстека до Backend Platform Developer в SMIT.Studio.
🟣 Международный исследовательский опыт
Работал исследователем в Институте ИИ НИУ ВШЭ и в Национальном университете Сингапура (NUS).
🟣 Преподаватель-практик
Ведет семинары в НИУ ВШЭ, в том числе по проектированию и разработке агентских систем.
🟣 Мастер интеграции AI в Backend
Его главная суперсила — умение правильно встраивать LLM через API, выстраивать workflow и агентную логику в сложных распределенных системах.
🔗 Зарегистрироваться на вебинар