Представьте, что вы разрабатываете несложную RPG-игру в жанре фэнтези. В вашей игре есть два типа персонажей: маги и воины.
Задача:
- Создайте двух магов (у каждого есть имя и мана)
- Создайте двух воинов (у каждого есть имя и выносливость)
- Реализуйте функцию
CastSpell— маг произносит заклинание, тратя 5 маны. Если маны недостаточно — выведите сообщение об ошибке - Реализуйте функцию
Attack— воин проводит атаку, тратя 5 выносливости. Если выносливости недостаточно — выведите сообщение об ошибке
Используйте только базовые знания: переменные, функции, условия.
Окей, код из разведки боем работает — маги колдуют, воины бьют. Но если вы внимательно посмотрели на решение, то наверняка почувствовали: что-то тут не так. Давайте разберёмся, что именно.
В предыдущей задаче маг мог вызвать функцию воина, а воин — функцию мага:
// Воин произносит заклинание? Это же не маг вовсе!
warrior1Stamina = CastSpell(warrior1Name, warrior1Stamina);
// Маг рубится мечом? Тоже нелепо!
mage1Mana = Attack(mage1Name, mage1Mana);
Синтаксически всё корректно — код компилируется. Но логически это ошибка. Нам нужно как-то изолировать характеристики и функции мага от характеристик и функций воина.
Вторая проблема — каждый новый персонаж требует отдельного набора переменных:
// Маг 1
string mage1Name = "Альберт";
int mage1Mana = 20;
// Маг 2
string mage2Name = "Мерлин";
int mage2Mana = 15;
// Маг 3 — и дальше...
string mage3Name = "Моргана";
int mage3Mana = 12;
Представьте, что нужно добавить каждому магу уровень и школу магии. Придётся заводить переменные mage1Level, mage2Level, mage3Level... Это тупиковый путь развития.
Процедурный подход отлично подходит для обучения и создания небольших программ. Однако для масштабных проектов, где важны гибкость и структурированность, он становится неэффективным. Далее мы переходим к изучению нового стиля программирования.
Чтобы понять суть ООП, обратимся к жизненному примеру.
Допустим, вы работаете в крупной компании кадровиком. В компании 500 сотрудников, и вам нужно хранить информацию о каждом: имя, должность, зарплата, дата выхода на работу.
Вариант 1 — журнал (процедурный): вы записываете всё подряд в общую тетрадь. Иванов — маркетолог, 50 000. Петров — программист, 80 000. Сидорова — бухгалтер, 60 000. Вы догадываетесь, как неудобно. Найти кого-то — долго. Добавить новое поле (например, номер кабинета) — значит перелопатить все записи.
Вариант 2 — картотека (объектно-ориентированный): вы создаёте шаблон карточки сотрудника с фиксированными полями: имя, должность, зарплата, дата выхода. Потом для каждого сотрудника заполняете отдельную карточку по этому шаблону. Каждая карточка — самостоятельный элемент, с упорядоченной информацией.
В программировании:
- Шаблон карточки — это класс
- Заполненная карточка конкретного сотрудника — это объект
Идея ООП основана на наблюдении за реальным миром. Всё вокруг состоит из объектов, и каждый объект обладает своими свойствами и поведением.
Возьмём телефон. Компания Apple разработала спецификацию и чертежи iPhone — это класс. А конкретный iPhone в вашем кармане — это объект, созданный по этим чертежам, с конкретным объёмом памяти, серийным номером и установленными приложениями.
Или автомобиль. Класс описывает набор общих характеристик: марка, цвет, мощность, количество дверей. А ваш конкретный автомобиль во дворе — это объект с конкретными значениями этих характеристик.
Мы с вами будем создавать такие же шаблоны данных (классы), на основании которых будем генерировать конкретных персонажей — магов или воинов (объекты).
Процедурный подход — стиль программирования, при котором программа представляет собой последовательность инструкций, а данные и функции существуют отдельно друг от друга.
ООП (объектно-ориентированное программирование) — подход, при котором программа строится вокруг объектов, объединяющих данные и поведение.
Класс — шаблон (чертёж), описывающий свойства и поведение будущих объектов.
Объект — конкретный экземпляр класса с собственными значениями свойств.
Четыре ключевых принципа ООП: инкапсуляция, наследование, полиморфизм, абстракция.