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

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

Відкрити в Telegram

Вопросы с собеседований по Java и ответы на них. По рекламе: @proglib_adv Учиться у нас: https://proglib.io/w/08c603b6 Для обратной связи: @proglibrary_feeedback_bot

Показати більше
6 485
Підписники
+124 години
+87 днів
+1330 день
Архів дописів
Что такое type erasure и какие ограничения он накладывает на дженерики? generics дают типобезопасность на этапе компиляции, но в рантайме информация о типовых параметрах стирается: List<String> превращается в обычный List, а компилятор сам вставляет приведения типов. Сделано ради обратной совместимости со старым кодом. Из-за стирания нельзя:создать new T() или new T[];использовать instanceof List<String>;иметь статические поля типа T;объявить два метода, отличающиеся только параметром типа (после стирания их сигнатуры совпадут). Частичная информация о дженериках сохраняется в сигнатурах полей и методов и доступна через рефлексию, но не на уровне конкретных экземпляров. 🐸 Библиотека собеса по Java #core

⚡️ Продолжаем знакомить вас с экспертами курса AgentOps! — Сергей Нотевский расскажет, как выстроить FinOps для AI-продуктов:
⚡️ Продолжаем знакомить вас с экспертами курса AgentOps!Сергей Нотевский расскажет, как выстроить FinOps для AI-продуктов: оптимизировать затраты на разработку и продакшен, внедрить model routing, semantic cache и систему алертов для контроля расходов — Эмиль Сатаев разберет Context Engineering: управление контекстом, защиту от prompt injection, работу с длинными контекстами и построение безопасного пайплайна входа для AI-систем — Михаил Бондаревский покажет, как подготовить инфраструктуру для AI-агентов: Docker, sandboxing, streaming, docker-compose и воспроизводимое окружение для разработки и продакшена — Мурат Хажгериев расскажет про Enterprise Integrations & MCP: когда MCP действительно нужен, как подключать внешние сервисы и реализовывать интеграции с OAuth2 delegation — Герман Сабиров разберет Governance & Compliance для AI-систем: data flow, audit logs, требования 152-ФЗ, локализацию данных и построение compliance-подхода на уровне архитектуры Курс для backend-разработчиков, тимлидов и LLM инженеров о том, как внедрять AI-логику в бэкенд IT-продуктов и сохранять стабильность сервиса. 👉 Изучить обновленную программу AgentOps и занять место.

Возможна ли утечка памяти, несмотря на сборщик мусора? Да, возможна. GC удаляет только недостижимые объекты. Утечка — это когда объекты остаются достижимыми, но больше не нужны, и память не освобождается. Типичные источники:неограниченные кеши и статические коллекции (static Map), которые растут без удаления;незакрытые ресурсы (соединения, стримы);слушатели/колбэки без отписки; ThreadLocal в пуле потоков, где значение не очищается через remove() (поток переиспользуется и не умирает);утечка класслоадеров при горячей перезагрузке. 🐸 Библиотека собеса по Java #core

Что такое escape analysis и какие оптимизации он даёт? Это анализ JIT-компилятора, который определяет, «убегает» ли объект за пределы метода или потока. Если объект создаётся и используется только локально и наружу не уходит, JVM может: → Scalar replacement — вообще не создавать объект в куче, а разложить его на отдельные поля в регистрах/на стеке. Это снимает нагрузку на GC. → Lock elision — убрать синхронизацию, если объект гарантированно не виден другим потокам. 🐸 Библиотека собеса по Java #jvm

Что происходит с объектом, когда на него не осталось ссылок? Объект становится кандидатом на сборку мусора, но память освобождается не сразу. GC ориентируется на достижимость от GC roots (стек потоков, статические поля, JNI-ссылки, активные локальные переменные). Пока объект достижим хотя бы по одной цепочке — он живой. Как только он перестаёт быть достижимым, он помечается недостижимым и память освобождается при ближайшем цикле сборки. Если у объекта есть finalize() или он зарегистрирован в Cleaner/PhantomReference, освобождение откладывается на дополнительный цикл. 🐸 Библиотека собеса по Java #core

💬 Обратная связь Текущий уровень сложности вопросов? 🔥 — Слишком просто, хочу сложнее 👍🏼 — В самый раз ❤️ — Иногда сложновато 😁 — Часто не понимаю 🐸 Библиотека собеса по Java

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

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

До 31 мая можно забрать любой курс Proglib Academy со скидкой 40% Если давно хотели прокачаться в Python, ML, алгоритмах или
До 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

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