Объектно-ориентированное программирование
В своё время мне было непросто освоить объектно-ориентированное программирование (ООП). Я изучал различные источники, но, возможно, они были слишком сложны, или мне не хватало смекалки и специального образования — так что я уловил лишь основные черты ООП.
Когда я, наконец, разобрался в этой теме, мне показалось, что ООП в действительности не такое уж сложное направление. Всё дело в том, чтобы объяснить его правильно и просто, человеческим языком.
Эта статья — попытка рассказать о объектно-ориентированном программировании доступно, для тех, кто не имеет специализированного образования.
Было бы предсказуемо начать обсуждение с принципов объектно-ориентированного программирования, таких как:
Однако я начну с классов и экземпляров классов, а также всего, что к ним относится (например, объектов). Это объясняется тем, что все четыре принципа в большей степени относятся именно к классам. Примеры, которые я буду приводить, также будут показаны в контексте использования классов.
Классы в ООП
Класс и экземпляр класса — это два фундаментальных понятия в объектно-ориентированном программировании, и понимание их различий важно для работы с ООП.
Что такое класс?
Класс — это как чертеж или шаблон, который описывает, как объекты должны быть созданы.
Класс определяет свойства (атрибуты) и поведение (методы) будущих объектов. Классы предоставляют структуру и определяют, как объект будет выглядеть и действовать, но сами по себе не содержат данных или состояния.
Когда говорится, что "классы сами по себе не содержат данных или состояния", это означает, что класс — это всего лишь шаблон или чертёж, который описывает, какие свойства (атрибуты) и действия (методы) должны быть у объектов, созданных на его основе, но он не хранит конкретные значения этих свойств.
Думаю, текст выше может быть немного непонятным, поэтому давайте рассмотрим это на очень простом примере:
Представим, что класс — это как кулинарный рецепт для блюда. Рецепт описывает, какие ингредиенты вам понадобятся и что вам нужно делать, чтобы приготовить блюдо. Однако сам рецепт не содержит ингредиентов — это просто инструкции. То есть, рецепт сам по себе не имеет никакого вкуса и не может быть съеден.
- Класс в программировании аналогичен кулинарному рецепту. Он говорит, какие данные (ингредиенты) и методы (инструкции по приготовлению) должен иметь объект (блюдо), но не содержит реальных данных.
- Объект — это конкретное блюдо, приготовленное по рецепту. Каждый объект (блюдо) будет содержать реальные данные (ингредиенты, такие как мука, сахар, яйца) и его состояние может меняться (например, сырое тесто превращается в выпеченный пирог).
По другому объект в ООП часто называют "Экземпляр класса"
И снова пример, чтобы было понятно:
- Если у вас есть класс
Car
, который определяет, что у автомобиля должны быть такие свойства, как марка, модель и цвет, и методы, например,start()
иstop()
, то когда вы создаёте конкретный автомобиль из этого класса, например, красный Ford Mustang, вы создаёте объект или экземпляр классаCar
.
Этот объект (Car
) будет иметь конкретные значения для своих свойств
марка: Ford,
модель: Mustang,
цвет: красный
и сможет выполнять действия, определённые методами класса (запускаться и останавливаться. Ну, или что вы еще определите при создании класса Car
).
Тем, кому интересно, как создание класса выглядит в коде Python, пример ниже:
class Car: # создаем класс Автомобиль def __init__(self, color, brand): self.color = color # цвет автомобиля self.brand = brand # марка автомобиля def drive(self): print(f"The {self.color} {self.brand} is driving.")
А вот код на Python для создания экземпляра класса на основе определенного выше класса Car:
# Создание объекта класса Car my_mustang = Car("red", "Ford Mustang") # Теперь можно вызвать метод drive, чтобы "поездить" на машине my_mustang.drive()
my_mustang
это переменная, которая хранит ссылку на новый объект классаCar
.Car("red", "Ford Mustang")
создает новый экземпляр классаCar
с цветом "red" и маркой "Ford Mustang".- Вызов
my_mustang.drive()
запустит выполнение методаdrive
, который определен в классеCar
, и выведет сообщение "The red Ford Mustang is driving."
В диаграмме классов UML это будет выглядет вот так:
Существует еще одно понятие класса - Абстрактный класс, которое я сейчас разберу и мы вернемся к принципам ООП.
По большому счету, абстрактный класс - это такой же простой класс, однако он не предназначен для создания экземпляров напрямую.
Вместо этого он предоставляет базовый шаблон для других классов с определением методов, которые должны быть реализованы в классах-наследниках.
Абстрактные классы часто содержат один или несколько абстрактных методов, которые не имеют реализации в самом абстрактном классе, но должны быть определены в его подклассах.
Например:
Рассмотрим "Транспортное средство" как абстрактный класс.
Один из обязательных абстрактных методов — это "запустить двигатель". Разные виды транспортных средств, такие как "Автомобиль" и "Мотоцикл", будут реализовывать этот метод по-разному.
Например, запуск двигателя автомобиля может включать проверку систем безопасности и электроники, тогда как для мотоцикла этот процесс может быть более прямым и простым.
Ниже диаграмма классов, которая показывает абстрактный класс Vehicle, наследуемые подклассы Car и Motorcycle, и объет класса Car - Toyota, и объект класса Motorcycle - HarleyDavidson.
Кстати, в этом примере образования подклассов от абстракного класса мы познакомились с одним из принципов ООП - наследованием.
А теперь подробнее.
Наследование
Наследование в программировании — это когда один класс берет свойства и функциональность от другого класса, и может добавлять или изменять их.
Снова примеры:
Автомобили:
Представь, что у нас есть базовый класс "Автомобиль", который имеет свойства как "скорость", "цвет" и "количество колес". Теперь, если мы хотим создать спортивный автомобиль, мы просто "наследуем" все эти свойства от базового автомобиля и добавляем новые характеристики, например, "спойлер" или "турбонаддув".
Животные: Если у нас есть базовый класс "Животное" с свойствами "еда", "способы передвижения", и "окрас", мы можем создать подкласс "Птица", который наследует все эти свойства и добавляет новые, как "летает" или "имеет перья".
Смартфоны:
Допустим, у нас есть базовый класс "Смартфон" с функциями "звонить", "принимать сообщения" и "серфить интернет". Теперь, если мы хотим создать "Игровой смартфон", мы можем наследовать все эти функции и добавить новые, такие как "высокая производительность" или "специализированные игровые кнопки".
Сладости: У нас есть базовый класс "Сладость" с характеристиками "вкус", "цвет" и "форма". Мы можем создать подкласс "Конфета", который наследует эти характеристики и добавляет новые, например, "жевательная" или "с начинкой".
Абстракция
Абстракция в программировании — это способ упростить сложные системы, выделяя важнейшие характеристики объекта и исключая те, которые не касаются текущего контекста.
Тут можно вспомнить создание абстрактного класса, про который говорилось выше, а можно посмотреть на примеры ниже:
В системе оплаты, например в интернет-магазине, абстракция может использоваться для упрощения процесса оплаты.
Вместо того чтобы каждый раз подробно описывать процесс взаимодействия с различными платежными системами (кредитные карты, электронные деньги, банковские переводы и т.д.), вы можете создать абстрактный класс "Оплата", который будет иметь общий метод "произвести оплату".
Конкретные классы (подклассы от абстрактного класса "Оплата"), такие как "ОплатаКартой" или "ЭлектронныеДеньги", будут реализовывать этот метод по-разному, но пользователь интерфейса системы будет работать с этими методами через общий интерфейс, не вдаваясь в детали каждой платежной системы.
Полиморфизм
Полиморфизм в программировании — это принцип объектно-ориентированного программирования, который позволяет объектам разных типов обрабатываться как объекты одного типа.
Другими словами, это способность одного и того же метода работать по-разному в зависимости от того, к какому объекту он применяется.
Например, если мы определим метод "говорить" для класса "animal", то при реализации этого методами экземпляром класса "тигр", мы получим рычание, а в случае класса "кот" - мяукание.
Это наверное самый простой принцип для понимания.
Однако еще одна ремарка, чтобы не перепутать абстракцию и полиморфизм:
Абстракция — это когда ты определяешь общую функцию для всех (например, "говорить"), а полиморфизм — это когда каждый объект (в нашем случае, каждое животное) реализует эту функцию по-своему.
Инкапсуляция
Инкапсуляция — это принцип сокрытия деталей реализации. Представьте, что вы используете телевизор. Вам не нужно знать, как он устроен внутри, чтобы пользоваться его функциями. Все сложности скрыты внутри устройства. Это позволяет также позволяет защитить данные от неправильного использования и упростить взаимодействие с объектом.
Представим, что у тебя есть коробка с игрушками. Ты хочешь, чтобы кто-то мог играть с игрушками, но только если он знает правила, как этими игрушками пользоваться. Ты не хочешь, чтобы кто-то взял твою любимую игрушку и сломал её. Поэтому ты ставишь замок на коробку и раздаешь ключи только тем друзьям, которым доверяешь и которые знают правила.
В программировании это значит, что код устроен так, что сохраняет важные данные (как твои игрушки) в безопасности и позволяет к ним доступ через специальные функции или методы (это как ключи к коробке), которые знают, как правильно с этими данными обращаться, чтобы ничего не сломать. Это помогает сделать программу удобной для использования и безопасной от ошибок.
Пример инкапсуляции в банковском проекте:
У каждого клиента есть банковский счет, и связанная с ним информация, такая как баланс счета, номер счета и история транзакций. Эти данные чувствительны и должны быть защищены от несанкционированного доступа.
- Банковский счет (класс): Это класс в нашей программе, который хранит все детали счета клиента. Атрибуты (свойства) класса, такие как баланс счета и номер счета, делаются приватными. Это означает, что они не могут быть изменены напрямую извне класса; они могут быть доступны только через методы (функции) этого класса.
- Публичные методы: Чтобы взаимодействовать с данными счета, мы предоставляем публичные методы, такие как
получитьБаланс()
,внестиДеньги(сумма)
иснятьДеньги(сумма)
. Эти методы управляют данными безопасно, обеспечивая, например, что не будет снято больше денег, чем есть на счету. - Приватные методы: Могут быть и приватные методы, например, для внутренних расчетов или для проверки условий перед выполнением транзакции. Эти методы не доступны извне класса, что повышает безопасность.
Таким образом, благодаря инкапсуляции, все важные данные и методы обработки этих данных находятся внутри класса Банковский счет
. Это предотвращает случайное или недобросовестное изменение состояния счета и помогает поддерживать целостность данных. Это также упрощает обслуживание программы, так как изменения в одной части программы менее вероятно повлияют на другие части.
Другими словами инкапсуляция - это возможность устанавливать приватность аттрибутов т.е. скрывать детали реализации класса от внешних пользователей этого класса. Это помогает упростить использование класса, так как пользователь класса фокусируется только на предоставленном интерфейсе, а не на внутреннем устройстве.