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 лекция
Недоступна
Гибкая система оплаты в магазине 💰
Гибкая система оплаты в магазине 💰
Комментарии (3)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Ioanna Polyak Уровень 43
31 октября 2025
Схематично: . Shape / \ Circle Rectangle Зачем тут точка...
Ksanders Уровень 32
29 ноября 2025
серьозный баг нашли 😏
Grrbrr7 Уровень 19
14 января 2026
Зачем вообще такая сложная схема)