В чем ключевые различия между процедурным и объектно-ориентированным программированием (ООП)?

«В чем ключевые различия между процедурным и объектно-ориентированным программированием (ООП)?» — вопрос из категории ООП, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Процедурное программирование (Procedural Programming) структурирует программу как последовательность выполняемых инструкций и вызовов процедур (функций). Данные и функции, которые их обрабатывают, разделены.

Объектно-ориентированное программирование (Object-Oriented Programming) организует программу вокруг объектов, которые объединяют данные (поля) и поведение (методы) в единое целое.

Сравнительная таблица: Аспект Процедурный подход Объектно-ориентированный подход
Основная единица Функция / Процедура Объект / Класс
Организация данных Данные часто глобальны или передаются в функции Данные инкапсулированы внутри объектов
Парадигма "Что делать?" (последовательность шагов) "Кто что делает?" (взаимодействие объектов)
Модификация и расширение Может быть сложной, приводит к изменению многих функций Упрощена через наследование и полиморфизм
Безопасность данных Низкая (данные часто открыты) Высокая за счет инкапсуляции (private поля)
Повторное использование На уровне функций и библиотек На уровне классов через наследование и композицию

Пример решения одной задачи двумя способами:

Процедурный стиль (C-подобный):

// Данные и логика разделены
struct Rectangle { int width; int height; };

int calculateArea(Rectangle rect) {
    return rect.width * rect.height;
}

void printArea(Rectangle rect) {
    System.out.println(calculateArea(rect));
}

// Использование
Rectangle r = {5, 10};
printArea(r);

Объектно-ориентированный стиль (Java):

// Данные и логика инкапсулированы в классе
class Rectangle {
    private int width;
    private int height;

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

    public int calculateArea() { // Метод работает с данными объекта
        return this.width * this.height;
    }

    public void printArea() {
        System.out.println(this.calculateArea());
    }
}

// Использование
Rectangle rect = new Rectangle(5, 10);
rect.printArea(); // Объект "сам знает", как вывести свою площадь

Три кита ООП, отсутствующие в чисто процедурном подходе:

  1. Инкапсуляция: Сокрытие внутреннего состояния объекта и деталей реализации.
  2. Наследование: Создание новых классов на основе существующих с возможностью переиспользования и расширения функциональности.
  3. Полиморфизм: Возможность объектов с одинаковым интерфейсом иметь разную реализацию.