JavaRush /Курсы /JAVA 25 SELF /Примеры построения абстракций в реальных задачах

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

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

1. Абстракция в реальных приложениях: зачем она нужна

В прошлых лекциях мы уже познакомились с понятием абстракции и посмотрели простые примеры. Теперь посмотрим, как этот подход работает в более реальных задачах. В реальных проектах почти всегда приходится иметь дело с разными, но похожими объектами. Например, разные способы оплаты, разные виды транспорта, разные фигуры в графическом редакторе. Если не применять абстракцию, код быстро превращается в набор «if-else» и копипасты. С абстракцией же — всё строго, красиво и, главное, удобно для развития и поддержки.

Абстракция позволяет:

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

Давайте разберём несколько примеров из разных областей.

2. Пример 1: Платёжные системы

Постановка задачи

Допустим, вы пишете модуль для интернет-магазина. Ваша задача — реализовать обработку разных видов платежей: банковская карта, PayPal, криптовалюта. Все они должны уметь «обрабатывать платёж», но детали у каждого свои.

Абстракция: класс Payment

public abstract class Payment {
    protected double amount;

    public Payment(double amount) {
        this.amount = amount;
    }

    // Абстрактный метод: как именно обрабатывать платёж — решают наследники
    public abstract void process();

    // Общий метод для всех платежей
    public void printAmount() {
        System.out.println("Сумма платежа: " + amount + " руб.");
    }
}

Конкретные реализации

public class CreditCardPayment extends Payment {
    private String cardNumber;

    public CreditCardPayment(double amount, String cardNumber) {
        super(amount);
        this.cardNumber = cardNumber;
    }

    @Override
    public void process() {
        System.out.println("Обработка платежа по карте: " + cardNumber);
        // Здесь могла бы быть интеграция с банком :)
    }
}
public class PaypalPayment extends Payment {
    private String email;

    public PaypalPayment(double amount, String email) {
        super(amount);
        this.email = email;
    }

    @Override
    public void process() {
        System.out.println("Обработка PayPal-платежа для аккаунта: " + email);
        // А тут — вызов PayPal API
    }
}
public class CryptoPayment extends Payment {
    private String walletAddress;

    public CryptoPayment(double amount, String walletAddress) {
        super(amount);
        this.walletAddress = walletAddress;
    }

    @Override
    public void process() {
        System.out.println("Обработка криптоплатежа на кошелёк: " + walletAddress);
        // Тут могла бы быть магия блокчейна
    }
}

Использование абстракции

import java.util.*;

public class PaymentDemo {
    public static void main(String[] args) {
        List<Payment> payments = new ArrayList<>();
        payments.add(new CreditCardPayment(1500.0, "1234 5678 9012 3456"));
        payments.add(new PaypalPayment(500.0, "user@example.com"));
        payments.add(new CryptoPayment(0.05, "0xABCD..."));

        for (Payment payment : payments) {
            payment.printAmount();
            payment.process();
            System.out.println("---");
        }
    }
}

Результат работы:

Сумма платежа: 1500.0 руб.
Обработка платежа по карте: 1234 5678 9012 3456
---
Сумма платежа: 500.0 руб.
Обработка PayPal-платежа для аккаунта: user@example.com
---
Сумма платежа: 0.05 руб.
Обработка криптоплатежа на кошелёк: 0xABCD...
---

Преимущества:

  • Можно добавить новый способ оплаты, не меняя старый код (например, Apple Pay).
  • Код, который работает с платежами, не зависит от их конкретного типа.
  • Общая логика (например, печать суммы через printAmount()) реализована в одном месте.

3. Пример 2: Транспорт

Постановка задачи

В игре или симуляторе у вас есть разные виды транспорта: машины, велосипеды, поезда. Все они могут «двигаться», но делают это по-разному. Некоторые требуют заправки, другие — нет.

Абстракция: класс Transport

public abstract class Transport {
    protected String name;

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

    public abstract void move();

    // Не все виды транспорта нуждаются в заправке, но по умолчанию — нет
    public void fuelUp() {
        System.out.println(name + ": заправка не требуется.");
    }
}

Конкретные реализации

public class Car extends Transport {
    public Car(String name) {
        super(name);
    }

    @Override
    public void move() {
        System.out.println(name + " едет по дороге.");
    }

    @Override
    public void fuelUp() {
        System.out.println(name + ": заправляем бензином.");
    }
}
public class Bicycle extends Transport {
    public Bicycle(String name) {
        super(name);
    }

    @Override
    public void move() {
        System.out.println(name + " крутит педали.");
    }
    // fuelUp не переопределяем — велосипеду не нужна заправка
}
public class Train extends Transport {
    public Train(String name) {
        super(name);
    }

    @Override
    public void move() {
        System.out.println(name + " мчится по рельсам.");
    }

    @Override
    public void fuelUp() {
        System.out.println(name + ": заправляем дизелем или электричеством.");
    }
}

Использование абстракции

import java.util.*;

public class TransportDemo {
    public static void main(String[] args) {
        List<Transport> vehicles = Arrays.asList(
            new Car("Toyota"),
            new Bicycle("Stels"),
            new Train("Сапсан")
        );

        for (Transport t : vehicles) {
            t.move();
            t.fuelUp();
            System.out.println("---");
        }
    }
}

Результат работы:

Toyota едет по дороге.
Toyota: заправляем бензином.
---
Stels крутит педали.
Stels: заправка не требуется.
---
Сапсан мчится по рельсам.
Сапсан: заправляем дизелем или электричеством.
---

Преимущества:

  • Можно обрабатывать любой транспорт одинаково, не проверяя его тип.
  • Легко добавить новый вид транспорта (например, электросамокат).

4. Пример 3: Графический редактор

Постановка задачи

Вы пишете мини-графический редактор. В нём есть линии, эллипсы, многоугольники — и всё это «фигуры», которые можно нарисовать и изменить размер. При этом каждая фигура реализует эти действия по-своему.

Абстракция: класс Figure

public abstract class Figure {
    protected String color = "black";

    public abstract void draw();

    public abstract void resize(double factor);

    public void setColor(String color) {
        this.color = color;
    }
}

Конкретные реализации

public class Line extends Figure {
    private double length;

    public Line(double length) {
        this.length = length;
    }

    @Override
    public void draw() {
        System.out.println("Рисуем линию длиной " + length + " цветом " + color);
    }

    @Override
    public void resize(double factor) {
        length *= factor;
        System.out.println("Новая длина линии: " + length);
    }
}
public class Ellipse extends Figure {
    private double a, b;

    public Ellipse(double a, double b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public void draw() {
        System.out.println("Рисуем эллипс с осями " + a + " и " + b + " цветом " + color);
    }

    @Override
    public void resize(double factor) {
        a *= factor;
        b *= factor;
        System.out.println("Новые размеры эллипса: " + a + " x " + b);
    }
}
public class Polygon extends Figure {
    private int sides;

    public Polygon(int sides) {
        this.sides = sides;
    }

    @Override
    public void draw() {
        System.out.println("Рисуем многоугольник с " + sides + " сторонами цветом " + color);
    }

    @Override
    public void resize(double factor) {
        System.out.println("Изменяем размер многоугольника с " + sides + " сторонами на " + factor);
    }
}

Использование абстракции

import java.util.*;

public class EditorDemo {
    public static void main(String[] args) {
        List<Figure> figures = new ArrayList<>();
        figures.add(new Line(10));
        figures.add(new Ellipse(5, 3));
        figures.add(new Polygon(6));

        for (Figure f : figures) {
            f.setColor("green");
            f.draw();
            f.resize(2);
            System.out.println("---");
        }
    }
}

Результат работы:

Рисуем линию длиной 10.0 цветом green
Новая длина линии: 20.0
---
Рисуем эллипс с осями 5.0 и 3.0 цветом green
Новые размеры эллипса: 10.0 x 6.0
---
Рисуем многоугольник с 6 сторонами цветом green
Изменяем размер многоугольника с 6 сторонами на 2.0
---

Преимущества:

  • Все фигуры можно хранить в одном списке и обрабатывать одинаково.
  • Легко добавить новую фигуру (например, звезду или сердце).
  • Общие методы (например, установка цвета через setColor()) реализованы один раз.

5. Как абстракция помогает упростить код

В каждом примере выше есть общая схема:

  • Базовый абстрактный класс задаёт контракт (что умеет объект).
  • Конкретные наследники реализуют детали.
  • Код, работающий с абстракцией, не зависит от типа объекта, что делает систему гибкой и расширяемой.

Таблица сравнения подходов

Без абстракции (if-else) С абстракцией (ООП)
Много условий по типу Новый тип — меняем код
Дублирование логики Логика — в одном месте
Трудно расширять Добавление — легко
Трудно тестировать Легко подменять реализации

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

Ошибка №1: Абстракция ради абстракции.
Если у вас всего один тип объекта и не планируется расширение — абстрактный класс не нужен. Не стоит усложнять код без причины.

Ошибка №2: Слишком общая абстракция.
Если базовый класс слишком «размытый», наследники могут не иметь ничего общего, кроме имени. Например, абстракция «Thing» для всего подряд. Это затрудняет поддержку и понимание кода.

Ошибка №3: Дублирование кода в наследниках.
Если у всех наследников одинаковая реализация метода, его стоит вынести в базовый класс (сделать не абстрактным).

Ошибка №4: Нарушение принципа «от общего к частному».
Если в абстрактном классе появляются детали, которые нужны только одному наследнику — значит, абстракция выбрана неверно.

Ошибка №5: Забыли реализовать абстрактные методы.
Если не реализовать все абстрактные методы в наследнике, компилятор заставит сделать класс тоже абстрактным. Иногда это неожиданно :)

1
Задача
JAVA 25 SELF, 19 уровень, 3 лекция
Недоступна
Расчеты площади в графическом приложении 🎨
Расчеты площади в графическом приложении 🎨
1
Задача
JAVA 25 SELF, 19 уровень, 3 лекция
Недоступна
Универсальная система рассылки сообщений 📧💬
Универсальная система рассылки сообщений 📧💬
1
Задача
JAVA 25 SELF, 19 уровень, 3 лекция
Недоступна
Управление автопарком компании 🚚🚲
Управление автопарком компании 🚚🚲
1
Задача
JAVA 25 SELF, 19 уровень, 3 лекция
Недоступна
Модуль обработки заказов в розничном магазине 🛒
Модуль обработки заказов в розничном магазине 🛒
Комментарии (6)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Alexey Ryabikov Уровень 31
7 февраля 2026
первая задача этой лекции- копия второй задачи прошлой лекции с измененными названиями и 3.14 вместо Math.PI. Ну и зачем так делать?
Kidze Уровень 26
7 января 2026
На написание "лора" задачи, который никто не читает уже к этому моменту, больше сил затрачено, чем на саму задачу. Ладно бы какой нибудь нюанс в каждой был интересный и полезный, но нет, одно и то же.
11 марта 2026
Вряд-ли на придумывание задач кто-то тратил силы. Нейросеткой же сделано.
Ksanders Уровень 32
29 ноября 2025
Спасибо что сегодня без зверушек 😏
Юрий Уровень 26
26 ноября 2025
уже 3-ья лекция подряд про одно и тоже. Зачем?
Ksanders Уровень 32
29 ноября 2025