JavaRush /Курсы /JAVA 25 SELF /Реализация абстракций и иерархий

Реализация абстракций и иерархий

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

1. Построение иерархии: от абстракции к деталям

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

В программировании, как и в жизни, всё начинается с вопросов. Например: «Что общего у круга и прямоугольника?» Ответ: они оба — фигуры. А что общего у фигур? Обычно у них есть площадь, и их можно нарисовать.

В Java это выражается через abstract-класс:

public abstract class Shape {
    public abstract double area();
    public abstract void draw();
}

Здесь мы говорим:

  • Любая фигура должна уметь считать свою площадь (area()).
  • Любая фигура должна уметь рисоваться (draw()).
  • Как именно она это делает — не наше дело (пока что).

Теперь создадим конкретные фигуры:

public class Circle extends Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public double area() {
        return Math.PI * radius * radius;
    }

    @Override
    public void draw() {
        System.out.println("Рисуем круг радиусом " + radius);
    }
}

public class Rectangle extends Shape {
    private double width, height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public double area() {
        return width * height;
    }

    @Override
    public void draw() {
        System.out.println("Рисуем прямоугольник " + width + "x" + height);
    }
}

Что мы сделали?

  • Вынесли общее в абстрактный класс.
  • Детализировали поведение в наследниках.

Схематично:


.          Shape
         /     \
     Circle   Rectangle

Таблица: что реализовано где

Класс area() draw() Свои поля
Shape
abstract
abstract
-
Circle реализовано реализовано
radius
Rectangle реализовано реализовано
width, height

2. Почему это удобно? (и почему это вообще работает)

Единый интерфейс для работы с разными объектами

Допустим, у вас есть коллекция фигур:

Shape[] shapes = {
    new Circle(5),
    new Rectangle(3, 4),
    new Circle(2.5)
};

Вы можете перебрать их одинаково, не думая о типе:

for (Shape shape : shapes) {
    shape.draw();
    System.out.println("Площадь: " + shape.area());
}

Пусть JVM сама разбирается, кто там круг, а кто прямоугольник! Это и есть полиморфизм (мы уже о нём говорили, и ещё будем говорить подробнее — в следующем блоке).

Лёгкость расширения

Захотелось добавить треугольник? Просто пишем (новый тип — старый код не меняем): Triangle extends Shape.

public class Triangle extends Shape {
    private double base, height;

    public Triangle(double base, double height) {
        this.base = base;
        this.height = height;
    }

    @Override
    public double area() {
        return 0.5 * base * height;
    }

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

Весь остальной код (например, перебор списка фигур) менять не нужно.

Избежание дублирования кода

Если у всех фигур появится общее свойство (например, цвет), его удобно вынести в абстрактный класс:

public abstract class Shape {
    private String color = "чёрный";

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

    public abstract double area();
    public abstract void draw();
}

Теперь любой наследник — хоть круг, хоть треугольник — получит цвет «по наследству».

3. Практика: разрабатываем мини-графический редактор

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

Абстрактный класс Figure

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

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

    public abstract void draw();
    public abstract void resize(double factor);
}

Конкретные фигуры

public class Line extends Figure {
    private double length;

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

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

    @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 + " цветом " + getColor());
    }

    @Override
    public void resize(double factor) {
        a *= factor;
        b *= factor;
        System.out.println("Новые оси эллипса: " + a + ", " + b);
    }
}

Хотите добавить новый инструмент, например Polygon? Просто создайте новый класс — весь код редактора работает через абстрактный Figure.

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

Figure[] figures = {
    new Line(10),
    new Ellipse(5, 3)
};

for (Figure figure : figures) {
    figure.setColor("red");
    figure.draw();
    figure.resize(1.5);
}

Вывод:

Рисуем линию длиной 10.0 цветом red
Новая длина линии: 15.0
Рисуем эллипс с осями 5.0 и 3.0 цветом red
Новые оси эллипса: 7.5, 4.5

Визуализация иерархии


.            Figure
             /    \
          Line   Ellipse

4. Как избежать дублирования: общие поля и методы

Иногда у всех наследников есть не только общие методы, но и общие поля (например, координаты центра). Абстрактный класс — идеальное место для этого:

public abstract class Figure {
    private double x, y; // координаты центра

    public Figure(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public void moveTo(double newX, double newY) {
        x = newX;
        y = newY;
        System.out.println("Фигура перемещена в точку (" + x + ", " + y + ")");
    }

    public abstract void draw();
}

Теперь любые Line или Ellipse могут перемещаться, не реализуя этот метод заново.

5. Ещё пример: платёжные системы

Абстракция — это не только про фигуры! Представьте, что вы пишете систему обработки платежей.

Абстрактный класс Payment

public abstract class Payment {
    public abstract void process();
}

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

public class CreditCardPayment extends Payment {
    @Override
    public void process() {
        System.out.println("Обработка платежа с кредитной карты");
    }
}

public class PaypalPayment extends Payment {
    @Override
    public void process() {
        System.out.println("Обработка платежа через PayPal");
    }
}

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

Payment[] payments = {
    new CreditCardPayment(),
    new PaypalPayment()
};

for (Payment payment : payments) {
    payment.process();
}

Вывод:

Обработка платежа с кредитной карты
Обработка платежа через PayPal

6. Преимущества такого подхода

  • Единый интерфейс: можно работать с разными объектами по-одинаковому.
  • Расширяемость: добавление новых видов объектов не требует переписывания старого кода.
  • Минимум дублирования: общее вынесено в базовый абстрактный класс.
  • Гибкость: можно использовать коллекции абстрактных типов, не заботясь о деталях.

7. Пример из жизни: транспорт

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

public abstract class Transport {
    public abstract void move();
    public abstract void fuelUp();
}

Конкретные виды транспорта реализуют детали:

public class Car extends Transport {
    @Override
    public void move() {
        System.out.println("Машина едет по дороге");
    }

    @Override
    public void fuelUp() {
        System.out.println("Заправляем бензином");
    }
}

public class Bicycle extends Transport {
    @Override
    public void move() {
        System.out.println("Велосипед крутит педали");
    }

    @Override
    public void fuelUp() {
        System.out.println("Велосипед не нуждается в топливе, только в бутербродах для велосипедиста!");
    }
}

8. Полезная схема: как строить иерархию абстракций

[Абстрактный класс]
        |
   [Конкретный подкласс]
        |
   [Ещё более конкретный подкласс] (если нужно)

- Всё общее — вверху!
- Всё уникальное — внизу!

9. Типичные ошибки при реализации абстракций и иерархий

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

Ошибка № 2: Нарушение принципа «от общего к частному».
Иногда начинающие программисты начинают строить иерархию «от деталей», забывая об общем. В результате появляются странные классы вроде RedCircleWithShadow, которые плохо вписываются в общую структуру. Всегда сначала выделяйте абстракцию, а потом детали.

Ошибка № 3: Слишком глубокие иерархии.
Если у вас цепочка наследования длиннее 3–4 уровней, задумайтесь: не пора ли использовать композицию или интерфейсы вместо наследования?

Ошибка № 4: Принудительная реализация неактуальных методов.
Если абстрактный класс содержит слишком много абстрактных методов, неактуальных для некоторых наследников, возможно, стоит пересмотреть структуру. Например, не все транспортные средства нуждаются в методе fuelUp() (велосипеду он ни к чему).

Ошибка № 5: Путаница между абстрактным классом и интерфейсом.
Абстрактный класс — когда есть общее состояние и/или частичная реализация. Интерфейс — когда нужно только «пообещать» наличие методов, но не хранить данные и не реализовывать поведение. Не смешивайте эти подходы без необходимости.

1
Задача
JAVA 25 SELF, 19 уровень, 2 лекция
Недоступна
Симулятор транспорта: машина едет 🛣️
Симулятор транспорта: машина едет 🛣️
1
Задача
JAVA 25 SELF, 19 уровень, 2 лекция
Недоступна
Библиотека геометрических фигур 🔵⬛
Библиотека геометрических фигур 🔵⬛
1
Задача
JAVA 25 SELF, 19 уровень, 2 лекция
Недоступна
Базовая система управления персоналом 🧑‍💼
Базовая система управления персоналом 🧑‍💼
1
Задача
JAVA 25 SELF, 19 уровень, 2 лекция
Недоступна
Гибкая система оплаты в магазине 💰
Гибкая система оплаты в магазине 💰
Комментарии (7)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Gans Electro Уровень 26
26 июня 2026
Здесь ведь дело не в абстракций, а в том как работает наследование. Когда создается наследник он имеет внутри себя маленького гномика родителя. Да и вообще абстракция просто не дает создавать родителя. А остальное относится к наследованию
Gans Electro Уровень 26
26 июня 2026

Пусть JVM сама разбирается, кто там круг, а кто прямоугольник!
Это и есть полиморфизм (мы уже о нём говорили,
и ещё будем говорить подробнее — в следующем блоке).
То есть здесь реклама того что уже расскажут ниже? Честно заел этот ИИ. Неужели он не помнит что было лекцию назад? Автор конечно постарался
Boris Vasilenko Уровень 21
21 мая 2026
Уже скучаю по Гав! и Мяу! =)
DmitryMandgik #6469384 Уровень 23
10 марта 2026
В лекции "Как избежать дублирования кода", лучше бы подумали как избежать Дублирования материала в лекции)
Ioanna Polyak Уровень 44
31 октября 2025
Схематично: . Shape / \ Circle Rectangle Зачем тут точка...
Ksanders Уровень 32
29 ноября 2025
серьозный баг нашли 😏
Grrbrr7 Уровень 44
14 января 2026
Зачем вообще такая сложная схема)