Мы продолжаем рассказ об объектно-ориентированном программировании: зачем оно нужно и в чём его сила. Это скорее теория, чем необходимая ежедневная практика, но такие вещи приводят в порядок картину мира.
👉 Если вы пишете простые программы для себя, все эти вещи вам могут не пригодиться. Но если хотите стать профессионалом — добро пожаловать.
🤔 Это обновлённая версия статьи. В старой были огрехи, которые мы постарались исправить, чтобы статья стала яснее и корректнее.
Вспоминаем основные понятия
- Есть программа. В императивном программировании программа представляет собой последовательность заданий, которые мы даём компьютеру. Делай одно, потом второе, потом третье. Есть ещё функциональное программирование, но это немного другое.
- Сами задания простые, но из простых заданий можно сделать более сложные. Если такое более сложное задание как-то назвать, можно сказать, что это функция — мини-программа внутри программы.
- В функцию можно упаковать любые последовательности действий, которые вам часто нужны в программе: например сохранить файл особым образом, вывести что-то на особый экран или отправить электронное письмо на особый адрес.
- Внутри одних функций можно вызывать другие. Например, в таск-менеджере может быть функция «Закрыть задачу», внутри которой сначала сохраняется файл, потом отправляется письмо, потом всё выводится на особый экран.
- Проблема: когда много функций вызываются друг из друга, они переплетаются и создают много зависимостей. Если изменить работу одной из функций, могут посыпаться другие.
- Решение: стандартизировать подходы к работе функций. В частности, для этого придумали объектно-ориентированное программирование.
- В объектно-ориентированном программировании главная не функция, а объект. Объект можно представить как коробку с данными и функциями.
Что есть класс
Вы можете изготовить объект вручную из любых функций и данных, которые вам нужны. Но если вам для работы программы нужны десятки похожих друг на друга объектов, имеет смысл как-то их стандартизировать.
Если по-простому, то класс — это «чертёж», по которому вы можете изготовить объекты. Вы прописываете один класс, определяете его поведение и свойства, а потом даёте команду создать на основе этого класса нужное число объектов.
Например:
- У вас может быть класс для пользователя интернет-магазина. Класс будет подсказывать, что у пользователя есть имя, адрес, почта и пароль, а также он может совершать покупки с помощью таких-то функций. На основании этого класса вы будете регистрировать множество пользователей.
- Может быть класс для кнопки в интерфейсе. Вы знаете, что на кнопку можно нажимать и её можно отключать при определённых условиях. Вы описываете это поведение в классе, потом создаёте много кнопок в интерфейсе.
Пример: объекты, классы и функции в игре
Допустим, вы пишете компьютерную игру. У вас есть герой, которым управляет игрок, и много врагов, которыми управляет компьютер. И герой, и враги могут стрелять и лечиться.
Если бы вы писали всё на функциях, у вас были бы такие функции:
Герой_выстрелить();
Герой_подлечиться();
Враг1_выстрелить();
…
Враг99_выстрелить();
Враг1_подлечиться();
…
Враг99_подлечиться();
Это довольно большой зоопарк из функций, делать так не надо. Гораздо лучше создать объекты, а внутрь к ним положить нужные функции:
Герой.выстрелить();
Герой.подлечиться();
Враг1.выстрелить();
…
Враг99.подлечиться();
Чтобы не прописывать все эти функции и объекты вручную, мы создадим класс «Персонаж»:
Класс «Персонаж»{
имя: тут будет имя;
тип: герой или враг;
здоровье: 100;
функция «Выстрелить» {тут описываем, как стрелять};
функция «Подлечиться» {тут описываем, как лечиться};
}
Теперь мы можем создавать сколько угодно персонажей, например, так:
Новый Персонаж (имя:Герой);
Новый Персонаж (имя:Враг1, здоровье: 10);
Новый Персонаж (имя:Враг2, здоровье: 20);
Допустим, мы решили добавить в нашу игру систему инвентаря. Чтобы не ходить по всем нашим врагам и героям и не копипастить в них код, мы пропишем эту систему внутри класса:
Класс «Персонаж»{
имя: тут будет имя;
тип: герой или враг;
здоровье: 100;
функция «Выстрелить» {тут описываем, как стрелять};
функция «Подлечиться» {тут описываем, как лечиться};
инвентарь: [сапоги-скороходы, меч-кладенец];
функция «Сбросить_инвентарь» {как сбрасывать};
функция «Подобрать_предмет» {как подбирать};
}
Теперь у всех персонажей появился инвентарь. по умолчанию у всех там лежат сапоги и меч, но при создании персонажа это можно переопределить. Также у всех персонажей появилась возможность сбросить весь инвентарь или подобрать предмет с земли.
Заметьте, что всё это мы добавили в одном месте, а появилось всё сразу у всех. В этом сила класса.
Где применять классы и ООП, а где — функции
Если вы делаете простую программу, которую можно сделать тремя функциями — делайте. Или даже если программа станет сложнее, в ней будет много функций, но все они логично связаны и понятно, почему сделано именно так, — тоже хорошо. Нет ничего плохого в том, что вы не используете объектно-ориентированное программирование там, где можно обойтись без него.
А вот если у вас проект со множеством абстракций, взаимосвязей внутри и вы заранее не можете предсказать, когда что с чем будет взаимодействовать, то лучше использовать классы и все преимущества ООП. Возможно, код станет сложнее, но это не всегда так. С другой стороны, вы сможете реализовать такую логику, которую на функциях было бы сделать непросто.