Самая мощная штука в программировании.
Мы продолжаем рассказ об объектно-ориентированном программировании: зачем оно нужно и в чём его сила. Это скорее теория, чем необходимая ежедневная практика, но такие вещи приводят в порядок картину мира.
👉 Если вы пишете простые программы для себя, все эти вещи вам не нужны. Но если хотите стать профессионалом — добро пожаловать. Разговор будет сложным, но полезным.
Вспоминаем основные понятия
- Есть программа — это последовательность заданий, которые мы даём компьютеру.
- Сами задания простые, но из простых заданий можно сделать более сложные. Если такое более сложное задание как-то назвать, можно сказать, что это функция — мини-программа внутри программы.
- В функцию можно упаковать любые последовательности действий, которые вам часто нужны в программе: например, сохранить файл особым образом, вывести что-то на особый экран или отправить электронное письмо на особый адрес.
- Внутри одних функций можно вызывать другие. Так можно делать всё более и более сложные функции. Например, есть функция «сохранить пользователя в базу данных». Её можно вызвать изнутри функции «Зарегистрировать пользователя». А уже эту функцию можно вызвать изнутри функции «Обработать логин и пароль».
- Проблема: когда много функций вызываются друг из друга, они переплетаются и создают много зависимостей. Если изменить работу одной из функций, могут посыпаться все связанные.
- Решение: стандартизировать подходы к работе функций. В том числе для этого придумали объектно-ориентированное программирование. Там главное — не функция, а объект. Объект — это набор функций и данных. Объект можно стандартизировать с помощью понятия «класс».
- И вот мы здесь.
На примере зверей
Вспомним уроки биологии: царство животных, виды и подвиды. Там есть класс «млекопитающие». У этого класса есть свойство, которое отличает его от всех других: все млекопитающие вскармливают детей молоком.
Все подклассы, которые входят в класс млекопитающих, тоже вскармливают детёнышей молоком. В ООП это называется наследованием: всё, что относится к базовому классу, применимо и к подклассам.
Среди млекопитающих есть семейства волчьих, кошачьих и так далее. Это тоже классы. У этих классов есть свои методы, например, как они охотятся. У них есть свои свойства, например, характер и цвет шерсти. У них есть свои интерфейсы — как они общаются между собой и с внешним миром.
Каждый представитель семейства волчьих, например волк, — это конкретный объект на основе своего класса. Если мы знаем, как в этом классе реализован метод «Загнать добычу», то у конкретного волка этот метод будет работать точно так же.
Суть объектов в том, что нам не нужно каждый раз изучать или создавать его поведение или свойства — мы уже сделали это один раз в классе, а потом просто создаём объекты на его основе. И если нам понадобится изменить способ охоты на добычу у всех волков, нам достаточно сделать это только в одном месте — в описании класса. И дальше наступает магия: все объекты начинают вести себя по-новому, хотя мы их не трогали вообще, мы просто изменили их базовый класс.
Исходные данные
Допустим, мы пишем интернет-магазин с системой скидок. Нам нужно работать с пользователями — постоянными покупателями. Пользователь у нас будет объектом: у него есть имя, возраст и адрес доставки по умолчанию. Мы заведём класс, который поможет инициировать нового покупателя.
Python будет нашим объектным языком в этом примере. Зададим через него класс.
Важное замечание: первым параметром в конструкторе идёт служебное слово self. Оно нужно для того, чтобы класс мог работать со своими значениями. Это чисто служебное слово.
Здесь сказано: «Вот класс для покупателя. У него есть три свойства: имя, возраст и адрес». Теперь мы можем заводить новых покупателей одной строкой:
# Заводим покупателей
user1 = User('Вася',23,'Чебоксары)
user2 = User('Маша',19,'Белгород')
JavaScript у нас будет примером процедурного языка, поэтому мы в нём не будем создавать классы, а используем просто переменные и функции. Создадим тех же двух пользователей:
Глядя на код, кажется, что без классов проще: код получается короче и понятнее. Так и есть, но лишь для самых простых вещей. Как только мы начнём делать действительно сложную программу, без классов часто вообще не обойтись.
Важное объяснение про классы в JavaScript.
Мы знаем, что в JS тоже есть классы и с ними даже можно работать. Но для обучения и понимания принципов ООП классы в JS не очень подходят: в них всё сложно с private-переменными и видимостью; технически классы — это функции, да и с методом определения там не всё так просто. Поэтому мы выбрали Python как классический пример объектного подхода: строгие классы и все возможности ООП. В этой статье мы намеренно упрощаем использование JavaScript, чтобы показать, как работает чистый процедурный подход. Программисты, не ругайтесь на нас за это.
Усложняем задачу
Мы хотим, чтобы у нашего клиента появились бонусные баллы. Для этого нужно вписать в класс ещё одно свойство. Тогда при создании новых объектов-покупателей у них тоже будут бонусные баллы. То есть мы пойдём не в каждого покупателя и не будем каждому прописывать баллы, а сделаем это в классе.
И окончательно усложним: допустим, у нас четыре уровня программы лояльности: 0, 1, 2 и 3. И за каждые 10 000 бонусных баллов мы повышаем покупателю уровень на единицу, пока не достигнем максимального. Мы хотим, чтобы у покупателей появился метод add_bonus(), который добавляет бонусные баллы и при необходимости повышает уровень программы лояльности. И чтобы всё это работало одинаково на всех десяти покупателях.
В ООП-подходе нам понадобится:
- класс для покупателей, в нём пять свойств — имя, возраст, адрес доставки, число бонусных баллов и уровень бонусной программы;
- в классе метод add_bonus(), который повышает уровни лояльности и следит за тем, чтобы он у всех был не выше трёх;
- когда всё будет готово, мы инициируем покупателей, а потом начисляем им какое-то количество баллов.
В процедурном подходе мы просто будем создавать нужные переменные, функции, которые обрабатывают всякие события, а потом вызывать их в нужное время. Звучит проще, но это только на первый взгляд.
Класс для покупателей
Python. Вот как будет выглядеть класс с объектно-ориентированным подходом. Пока что без методов, только данные:
JavaScript. Так как мы договорились не использовать здесь классы, то создавать тоже пока нечего. На всякий случай напомним, как мы будем создавать переменные с покупателями, это пригодится нам на следующем шаге:
// Создаём первого покупателя
user1 = ['Вася',23,'Чебоксары'];
// Создаём второго покупателя
user2 = ['Маша',19,'Белгород'];
Начисляем бонусные баллы — метод add_bonus()
Задача этого кода — сделать так, чтобы определённый покупатель получил нужное количество бонусных баллов и использовать это в программе лояльности. Работает она так: за каждые 10 000 баллов даётся новый уровень, но максимальный уровень — 3, выше него получить ничего нельзя, даже если баллы позволяют.
Логика будет такая: берём бонусные баллы и прибавляем к тем, что уже есть у покупателя. Затем делим баллы на 10 000, берём целую часть от деления и делаем это уровнем программы лояльности. При этом проверяем, чтобы она не была больше трёх. Если уровень получается больше трёх — делаем принудительно тройку.
Посмотрите на последнюю строчку, где мы выводим сообщение. Мы сначала подготовили всю строку, а там, где должны быть имя и уровень программы лояльности, стоят фигурные скобки. Это значит, что мы должны назвать те переменные, которые туда по очереди подставятся — именно в той последовательности, в которой мы их зададим. Это делается командой .format, которую мы пишем сразу после нашей строки.
Плюс такого подхода в том, что это универсальный метод для каждого объекта, который создаётся на основе этого класса. Нам не нужно писать код для каждого покупателя — он работает для всех.
JavaScript. В процедурном программировании каждая переменная — это отдельная сущность, которая может требовать индивидуального подхода. Поэтому мы будем писать обработчик бонусных баллов для каждой переменной user1, user2 и так далее:
Сразу стало много кода — а это мы обработали всего двух покупателей. Представьте, что будет, когда покупателей станет хотя бы 10.
Финал: работаем с покупателями
Теперь смоделируем такую ситуацию: у нас появляется 5 покупателей, и некоторым из них мы начисляем бонусные баллы.
Python.
JavaScript. Сделаем то же самое на JS, не забывая прописать свой обработчик для каждой переменной:
Код делает то же самое, при этом он больше на 20%, хотя у нас всего 5 пользователей. Если мы добавим ещё 5, размер кода увеличится вдвое. А теперь представьте, что вам нужно добавить новое действие — списание бонусных баллов. В классе это сделать просто: в описании класса добавляем новый метод, и им могут пользоваться все объекты этого класса. А в процедурном программировании нам нужно будет прописывать столько же обработчиков, сколько и покупателей. Если у нас будет 100 покупателей, код превратится в ад.
Что дальше
Мы изучили, как работают классы, и немного программировали на Питоне. Следующий материал — про то, как устроен этот язык программирования и как его быстро освоить, если вы уже умеете писать на JavaScript.