JavaRush /Курсы /JAVA 25 SELF /Упрощение сложных систем с помощью абстракций

Упрощение сложных систем с помощью абстракций

JAVA 25 SELF
19 уровень , 4 лекция
Открыта

1. Многоуровневая абстракция

Когда вы только начинаете программировать, всё кажется простым: написал класс, вызвал метод, получил результат. Но в реальных проектах всё усложняется: десятки классов, сотни методов, тысячи строк кода... А если над проектом работает команда — задача усложняется ещё больше! Как не утонуть в этом хаосе?

Ответ — делить сложное на простое, а ещё лучше — на уровни абстракции.

Что такое уровни абстракции?

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

  • Пользовательский интерфейс (UI) — то, что видит пользователь.
  • Бизнес-логика — правила и процессы, которые реализуют суть приложения.
  • Доступ к данным (DAO, Repository) — работа с базой данных или файлами.

Каждый слой работает с абстракциями, не зная деталей других слоёв. Например, бизнес-логике не важно, как именно реализован интерфейс пользователя или как хранятся данные — ей важно, что есть методы типа saveOrder() или findUserById().

Аналогия из жизни

Представьте ресторан. Гости (UI) делают заказ через официанта (абстракция интерфейса), повар (бизнес-логика) готовит блюдо, а кладовщик (доступ к данным) следит за наличием продуктов на складе. Гости не знают, как именно повар готовит блюдо, а повар не интересуется, где лежит картошка — главное, чтобы она была под рукой.

2. Пример: многоуровневая архитектура на практике

Давайте разовьём наш учебный проект — например, приложение для учёта задач (task manager). Мы уже умеем создавать классы для задач, теперь усложним картину и разделим приложение на слои.

Выделяем абстракции

  • Task — абстрактное описание задачи: у задачи есть название, статус, методы для выполнения.
  • TaskRepository — абстракция для хранения задач (не важно, где — в памяти, файле, базе данных).
  • TaskService — бизнес-логика: добавление задач, поиск, выполнение.

Абстрактные классы и интерфейсы

// Слой бизнес-логики
public abstract class Task {
    private String title;
    private boolean completed;

    public Task(String title) {
        this.title = title;
        this.completed = false;
    }

    public abstract void complete();

    public String getTitle() { return title; }
    public boolean isCompleted() { return completed; }

    protected void setCompleted(boolean completed) { this.completed = completed; }
}

// Слой хранения данных (абстракция)
public interface TaskRepository {
    void save(Task task);
    Task findByTitle(String title);
    List<Task> findAll();
}

Реализация слоёв

Реализация Task

public class WorkTask extends Task {
    private String deadline;

    public WorkTask(String title, String deadline) {
        super(title);
        this.deadline = deadline;
    }

    @Override
    public void complete() {
        setCompleted(true);
        System.out.println("Рабочая задача '" + getTitle() + "' выполнена к сроку " + deadline);
    }
}

Реализация TaskRepository

public class InMemoryTaskRepository implements TaskRepository {
    private List<Task> tasks = new ArrayList<>();

    @Override
    public void save(Task task) {
        tasks.add(task);
    }

    @Override
    public Task findByTitle(String title) {
        for (Task task : tasks) {
            if (task.getTitle().equals(title)) {
                return task;
            }
        }
        return null;
    }

    @Override
    public List<Task> findAll() {
        return new ArrayList<>(tasks);
    }
}

Реализация TaskService

public class TaskService {
    private TaskRepository repository;

    public TaskService(TaskRepository repository) {
        this.repository = repository;
    }

    public void addTask(Task task) {
        repository.save(task);
    }

    public void completeTask(String title) {
        Task task = repository.findByTitle(title);
        if (task != null) {
            task.complete();
        } else {
            System.out.println("Задача не найдена: " + title);
        }
    }

    public void showAllTasks() {
        for (Task task : repository.findAll()) {
            System.out.println(task.getTitle() + " — " + (task.isCompleted() ? "выполнена" : "не выполнена"));
        }
    }
}

Использование в главном классе

public class Main {
    public static void main(String[] args) {
        TaskRepository repo = new InMemoryTaskRepository();
        TaskService service = new TaskService(repo);

        service.addTask(new WorkTask("Сделать отчёт", "2025-07-15"));
        service.addTask(new WorkTask("Подготовить презентацию", "2025-07-16"));

        service.showAllTasks();

        service.completeTask("Сделать отчёт");
        service.showAllTasks();
    }
}

Что мы получили?

  • Главный класс (Main) не знает, как устроено хранилище задач — он работает с абстракцией TaskRepository.
  • TaskService не знает, какие бывают задачи — он работает с абстрактным классом Task.
  • Если завтра мы захотим хранить задачи в базе данных, а не в памяти — просто реализуем новый класс DatabaseTaskRepository, не переписывая бизнес-логику и UI.
  • Если появится новый тип задачи, например, HomeTask, — просто добавим новый класс-наследник.

3. Преимущества для командной работы

В больших проектах редко бывает, что один человек пишет всё подряд. Обычно команда делится на "фронтендеров", "бэкендеров", "разработчиков хранилища" и т.д. Как абстракции помогают им не наступать друг другу на ноги?

Разделение ответственности

Каждый работает на своём уровне абстракции.

  • Один разработчик пишет реализацию TaskRepository для работы с базой.
  • Другой занимается бизнес-логикой (TaskService).
  • Третий пилит пользовательский интерфейс.

Контракт между слоями фиксируется абстракциями.

Пока все согласны с тем, что у TaskRepository есть методы save, findByTitle, findAll — детали реализации не важны.

Лёгкость тестирования и замены компонентов

  • Можно легко заменить одну реализацию на другую (например, для тестов использовать InMemoryTaskRepository, а в продакшене — работу с базой).
  • Тестировщик может подменить слой данных "заглушкой" (mock), чтобы тестировать бизнес-логику изолированно.

Независимость развития

  • Если кто-то решит добавить новый тип задачи, он не ломает существующий код — просто реализует новый подкласс Task.
  • Если появится новый способ хранения данных, меняется только реализация интерфейса, а остальной код не трогается.

4. Best practices: как не переборщить с абстракциями

Абстракция — как соль в блюде: без неё пресно, а если переборщить — испортишь всё. Вот несколько советов:

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

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

Старайтесь, чтобы абстракции были осмысленными.
Абстрактный класс должен выражать действительно общее поведение и/или состояние.

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

5. Абстракция в больших системах: пример из жизни

Рассмотрим, как абстракция работает в реально большом проекте — например, в интернет-магазине.

Слои системы

  • Контроллеры (UI): принимают запросы пользователя (например, "оформить заказ").
  • Сервисы (бизнес-логика): проверяют наличие товара, рассчитывают скидки, оформляют заказ.
  • Репозитории (доступ к данным): сохраняют заказы, товары, пользователей в базу данных.

Пример абстракций

// Абстракция для сервиса обработки заказов
public interface OrderService {
    void createOrder(Order order);
    Order findOrderById(String id);
}

// Абстракция для хранилища заказов
public interface OrderRepository {
    void save(Order order);
    Order findById(String id);
}

Каждый слой знает только о своей абстракции. Если завтра решат хранить заказы в облаке — меняется только реализация OrderRepository.

Взаимодействие слоёв — схема

[UI/Controller] <--> [OrderService (абстракция)] <--> [OrderRepository (абстракция)] <--> [База данных]
  • Каждый слой работает с абстракцией, не зная деталей нижележащего слоя.
  • Это позволяет разрабатывать, тестировать и дорабатывать каждый слой независимо.

Абстракция и поддержка кода

  • Легко добавлять новые возможности (новые виды задач, платежей, транспорта).
  • Легко исправлять ошибки (исправили баг в одном месте — все наследники получили обновление).
  • Легко тестировать (можно подменять слои на "заглушки" для юнит-тестов).

6. Типичные ошибки при проектировании абстракций

Ошибка №1: Избыточная абстракция. Иногда хочется сделать абстрактный класс на каждый чих. Но если у вас только один вид сущности, не стоит ради моды делать абстракцию — это только усложнит код.

Ошибка №2: Слишком размытая абстракция. Если ваш абстрактный класс описывает слишком много всего и не имеет чёткой области ответственности, его наследники будут вынуждены реализовывать ненужные методы или хранить "мертвые" поля.

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

Ошибка №4: Жёсткая связь между слоями. Если слой бизнес-логики напрямую зависит от конкретной реализации хранилища (например, использует new InMemoryTaskRepository() внутри себя), то при замене хранилища придётся переписывать весь код. Используйте абстракции (интерфейсы, абстрактные классы) для ослабления связей.

Ошибка №5: Недостаточная документация. Абстракция — это контракт, и его нужно чётко описывать. Если не написать, что должен делать наследник, легко получить неожиданные ошибки или "творчество" коллег.

1
Задача
JAVA 25 SELF, 19 уровень, 4 лекция
Недоступна
Создаем основу для менеджера задач ✍️
Создаем основу для менеджера задач ✍️
1
Задача
JAVA 25 SELF, 19 уровень, 4 лекция
Недоступна
Интеллектуальный сервис задач 🧠
Интеллектуальный сервис задач 🧠
1
Задача
JAVA 25 SELF, 19 уровень, 4 лекция
Недоступна
Строим модульный менеджер задач 🏗️
Строим модульный менеджер задач 🏗️
1
Задача
JAVA 25 SELF, 19 уровень, 4 лекция
Недоступна
Расширяем менеджер задач для разных типов задач 🚀
Расширяем менеджер задач для разных типов задач 🚀
1
Опрос
Абстрактные классы, 19 уровень, 4 лекция
Недоступен
Абстрактные классы
Абстракция и абстрактные классы
Комментарии (24)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Anonymous #6499111 Уровень 20
30 августа 2026
По сути, задачи сложные, но 2 задание решается копированием примера из лекции... 3 и 4 небольшим гуглингом(как сделать то, как сделать это, как это должно работать и тд). Считаю, что это не совсем правильный подход, тем более после предыдущих заданий. Концепция сама по себе хорошая, взяли проект и потихоньку его развиваем, делаем сложнее и больше, но все и сразу в одном уровне же...
KaRtOfElIo Уровень 26
23 августа 2026
не, задачи крутые, но лучше же постепенно набирать сложность... а не так резко... 13 попыток со второй задачей....
rwnmei Уровень 45
23 августа 2026
Переходишь сразу на комментарии что бы узнать норм лекция или нет но видимо эта лекция зачет ща узнаем
rwnmei Уровень 45
24 августа 2026
ну задачи прям интересные если лекцию хорошо прочитать и решить 2 задачу то остальные 2 решаются на изи 😎
grig_by Уровень 21
17 июля 2026
обучение: вот отвёртка, вот винт. отвёрткой можно закручивать винт. задача: построить синхрофазотрон.
Андрей Уровень 21
28 июня 2026
этот уровень не понравился, не сбалансированный.
nightmarakesh Уровень 45
15 июля 2026
Всяко лучше чем все лекции подряд читать и реализовывать одни и те же задачи про животных сотрудников и фигуры, тут реально прогресс ощутим,а не стагнация
Андрей Уровень 21
15 июля 2026
Лучше, но не хорошо
Gans Electro Уровень 42
26 июня 2026
Видимо лекция и задачи полностью человеческие. Фига реальные Spring-like задачи. Кайфанул от задач. Тут уже начинаешь понимать, как устроены реальные Spring-приложения. Конечно, это еще не полноценный MVC, но архитектура очень похожа: разделение на UI, Service и Repository с внедрением зависимостей.
Timofey Novitsky Уровень 36
18 мая 2026
Интересные задачи! Ура наконец то! Отдушина!
13 мая 2026
уже все прочитали коментарии про сложность данных задач по этому хочу обратить ваше внимание на то, что выполнены они в среднем за 2 попытки, по крайней мере вторая задача :) после такого хочется пройти тест iq, видимо он уже на уровне Мяу Мяу у меня
Иван Уровень 3
10 мая 2026
Надоели Animal, Cat и Dog? Держите взрыв мозга)
11 марта 2026
Наконец-то интересные задачи! Надеюсь, дальше в том же духе будет...