Включите исполнение JavaScript в браузере, чтобы запустить приложение.
14 окт 2024

Объектно-ориентированное программирование (ООП): что это такое, основные принципы и структура

Все, что нужно знать об объектно-ориентированном программировании. На каких принципах основывается ООП? Из чего состоит структура? Какие языки объектно-ориентированного программирования самые популярные? Плюсы и минусы ООП.
  1. ООП: что это такое
  2. Где и зачем нужно ООП
  3. Структура объектно-ориентированного программирования
  4. Основные принципы ООП
  5. Плюсы и минусы ООП
  6. Языки объектно-ориентированного программирования

Объектно-ориентированное программирование — это неотъемлемая часть современной разработки, хоть это и не единственный подход к написанию кода. В этой статье узнаем, что это, зачем нужно, какую имеет структуру и принципы, а также рассмотрим достоинства и недостатки.

ООП: что это такое

Объектно-ориентированное программирование — это парадигма, или методология, разработки, которая организует структуру программы вокруг объектов, взаимодействующих друг с другом.

Парадигма — это группа идей, правил и понятий, которые определяют стиль/подход к написанию кода. Это позволяет привести его к общему виду, что облегчает разработку и чтение кода, а также помогает избежать возможных ошибок.

Код, написанный в соответствии с этой парадигмой, достаточно легко воспринимается человеком, ведь объекты и классы можно перенести в реальный мир. Интуитивно понятно, что к классу «Человек» можно отнести каждого человека. Все люди имеют как общие характеристики — атрибуты, так и свойственные лишь конкретной группе людей — подклассу, или вообще только одному человеку — объекту. Принципы ООП можно перенести в реальный мир, например, имя и фамилию человека могут знать все, но менять их никто извне не может — этот пример характеризует инкапсуляцию.

Где и зачем нужно ООП

Можно сказать, что объектно-ориентированное программирование нужно для того, чтобы представлять сложную логику больших программ в виде отдельных и независимых друг от друга элементов — объектов. Также объектно-ориентированное программирование обеспечивает:

  • придание общей структуры и отсутствие запутанного кода;
  • взаимодействие между элементами;
  • легкое масштабирование, поддержку и внесение изменений в код;
  • уменьшение объема кода и его переиспользование.

ООП применяется для разработки самого разного программного обеспечения и ООСУБД, а также при создании игр и других проектов. Простой пример применения: организация работы интернет-магазина, где основные сущности — покупатели, товары, корзина и заказы представлены классами. Для каждой из этих сущностей предусмотрены свои характеристики и методы. Это хорошо структурирует код, обеспечивает возможности его расширения и поддержки.

Структура объектно-ориентированного программирования

Объектно-ориентированное программирование построено на четырех компонентах: классе, объекте, атрибуте и методе. Рассмотрим каждый из них:

  • Класс — это шаблон, описывающий атрибуты и методы объекта. Классы могут наследоваться. Например, существует класс «Животное» — он описывает атрибуты и методы всех животных. Дальше мы можем создать подклассы — «Собака», «Ящерица» и «Слон». Они могут содержать как родительские свойства, например, возраст животного, так и уникальные, нужные конкретно этому подклассу, например, длина хобота для слона или цвет для ящерицы. Реализуем это на языке программирования Python:
# Определение класса «Животное» с атрибутами имя и возраст
class Animal:
    def __init__(self, name, age):
        self.name = name  
        self.age = age  
 
# Определение подкласса «Собака», добавлен атрибут порода 
class Dog(Animal):
    def __init__(self, name, age, breed):
        super().__init__(name, age)  
        self.breed = breed  
 
# Определение подкласса «Ящерица», добавлен цвет
class Lizard(Animal):
    def __init__(self, name, age, color):
        super().__init__(name, age)  
        self.color = color 
 
# Определение подкласса «Слон», добавлена длина хобота
class Elephant(Animal):
    def __init__(self, name, age, trunk_length):
        super().__init__(name, age)  
        self.trunk_length = trunk_length  
py
  • Объект — это элемент со своими методами и свойствами, в контексте ООП это часто конкретный экземпляр класса. Возвращаясь к примеру выше: двухлетний лабрадор Шарик — это объект подкласса «Собака».
  • Атрибуты — это характеристики объекта, например, имя и возраст животного. То есть, при определении класса атрибуты просто указываются, а уже при создании объекта атрибуты получают свои значения.
  • Метод — это функция, которая обеспечивает взаимодействие с объектами и другими частями программы, либо определяет их поведение.

Теперь можно рассмотреть приближенный к реальности пример, охватывающий создание и применение каждого структурного элемента, — упрощенный вариант карточек пользователей.

Для начала определим класс «Пользователь» с атрибутами имя, фамилия и адрес электронной почты:

class User:
    def __init__(self, first_name, last_name, email):
        self.first_name = first_name  
        self.last_name = last_name  
        self.email = email 
py

Затем определим методы:

# get_first_name возвращает имя пользователя
    def get_first_name(self):
        return self.first_name
   
    # get_last_name возвращает фамилию пользователя
    def get_last_name(self):
        return self.last_name
  
    # get_email возвращает email пользователя
    def get_email(self):
        return self.email


   # get_full_name возвращает полное имя пользователя
    def get_full_name(self):
        return f"{self.first_name} {self.last_name}"


    # display_user_info выводит на экран полную информацию о пользователе
    def display_user_info(self):
        print(f"Полное имя: {self.get_full_name()}")
        print(f"Email: {self.email}")
py

Теперь создадим два объекта с заданными значениями атрибутов:

user1 = User("Мария", "Иванова", "mary.ivanova@example.com")
user2 = User("Игорь", "Андреев", "igor.andreev@example.com")
py

И воспользуемся методами:

print(user1.get_first_name())  
# Вывод: Мария


print(user1.get_last_name())  
# Вывод: Иванова


print(user1.get_email()) 
# Вывод: mary.ivanova@example.com


user2.display_user_info()
# Вывод: 
Полное имя: Игорь Андреев
Email: igor.andreev@example.com
py

Основные принципы ООП

Парадигма основывается на четырех принципах: абстракция, инкапсуляция, наследование и полиморфизм. Стоит сказать, что абстракцию не всегда учитывают как принцип ООП, но здесь, для более детального понимания, будет рассмотрена и она.

Абстракция

Абстракция в объектно-ориентированном программировании предусматривает выделение наиболее значимых характеристик и методов, при этом все детали реализации должны быть скрыты. То есть, объект должен быть предоставлен только с необходимыми, в данном случае, характеристиками и методами, но их детали и логика предоставляться не должны.

Иногда этот принцип еще описывают так: «абстракция отвечает на вопрос "Что?", без ответа на вопрос "Как?"». Возвращаясь к примеру о животных: для всех животных потребуется указать их имя и возраст, ведь абстрактное животное имеет эти характеристики, при этом объектам подкласса «Собака» не нужно свойство длина хобота, поэтому оно было определено только для подкласса «Слон».

Инкапсуляция

Инкапсуляция подразумевает то, что все необходимое для работы объекта хранится внутри него. То есть, все необходимое помещается в «капсулу» — класс, и ничто извне не может вносить изменения. Если возникнет надобность внесения изменений, то соответствующий метод должен быть определен внутри.

Инкапсуляция позволяет исключить возможность возникновения зависимостей одного класса от другого. Например, кто и что угодно может знать, как зовут и сколько лет конкретному животному, но изменять эту информацию он не может.

Наследование

Наследование — это принцип, который означает, что классы, а точнее, подклассы, могут наследовать методы и атрибуты родительского класса: все или только часть из них.

В примере про животных можно отследить этот принцип: все собаки являются животными, поэтому могут наследовать их атрибуты и методы, при этом собаки имеют и свои свойства, которые были добавлены, но не подходящие для каждого животного. Помимо этого, были созданы и другие подклассы: «Ящерица» и «Слон», так как они тоже являются животными.

В реальной жизни такие иерархии могут быть гораздо сложнее и больше, но это делает код понятным и облегчает масштабирование. Рассмотрим еще один пример наследования на языке Python, в котором характеристики и методы «родителя» будут наследоваться частично, а в подклассы будут добавлены новые:

# Уже знакомый класс «Животное», но с новым методом, который возвращает информацию о животных
class Animal:
    def __init__(self, name, age):
        self.name = name  
        self.age = age    
 
    def get_details(self):
        return f"Имя: {self.name}, Возраст: {self.age}."  
   
# Определение подкласса «Рыба», который наследует характеристики и get_details. Также добавлена характеристика «тип воды» (пресная или морская) и специальный метод для рыб, который возвращает информацию о воде, в которой плавает рыба
class Fish(Animal):
    def __init__(self, name, age, water_type):
        super().__init__(name, age)  
        self.water_type = water_type 
 
    def swim(self):
        return f"{self.name} плавает в {self.water_type} воде." 
   
fish = Fish("Эльза", 2, "морской")
print(fish.get_details())
# Вывод: 
Имя: Эльза, Возраст: 2.
print(fish.swim())
# Вывод: 
Эльза плавает в морской воде.
py

Полиморфизм

Полиморфизм подразумевает то, что один и тот же метод может выполняться по-разному в разных объектах. Это обеспечивает гибкость кода и помогает избежать ошибок. Как полиморфизм выглядит на практике:

# Класс «Животное» с новым методом, который возвращает звуки животных
# Именно его будем переопределять для достижения полиморфизма
class Animal:
    def __init__(self, name):
        self.name = name  
    def make_sound(self):
        return "обычный звук"  
 
# Определение подкласса «Собака», который переопределяет make_sound
class Dog(Animal):
    def make_sound(self):
        return "гав-гав!"
 
# Определение подкласса «Кошка», который переопределяет make_sound
class Cat(Animal):
    def make_sound(self):
        return "мяу!"
 
# Определение подкласса «Корова», который переопределяет make_sound
class Cow(Animal):
    def make_sound(self):
        return "му!"
 
# Напишем функцию, которая будет выводить на экран информацию о животных
def display_animal_info(animal):
    print(f"Это {animal.name}. Она говорит {animal.make_sound()}")
 
dog = Dog("Герда")
cat = Cat("Дуся")
cow = Cow("Ромашка")
 
# Вызов функции
display_animal_info(dog)  
# Вывод: Это Герда. Она говорит гав-гав!
display_animal_info(cat)  
# Вывод: Это Дуся. Она говорит мяу!
display_animal_info(cow)  
# Вывод: Это Ромашка. Она говорит му!
py

Плюсы и минусы ООП

Преимущества:

  • Организация кода. Следование правилам объектно-ориентированного программирования обеспечивает разделение программы на отдельные и понятные структуры/модули. Это позволяет работать над фрагментами программы независимо от других ее частей.
  • Облегчение поддержки и обновления программы. Для того чтобы скорректировать работу программы, нужно внести изменения только в одном месте и не искать по всему коду места, где тоже нужно что-то поменять, чтобы избежать ошибок и некорректной работы программы. Эти изменения также применяются ко всем наследуемым элементам. Например, если нужно изменить логику работы определенного метода — ее нужно изменить внутри класса, где метод был определен, вместо того, чтобы искать все части кода, где данный метод используется.
  • Расширяемость и гибкость. По мере роста системы можно добавлять новую функциональность и структурные элементы без необходимости вносить изменения в остальную часть программы. Также уже существующий код можно адаптировать под новые задачи благодаря полиморфизму.
  • Переиспользование кода. В ООП достаточно один раз определить класс, его характеристики и методы, а затем использовать этот код неограниченное количество раз благодаря наследованию. Это значительно ускоряет разработку и дает возможность сосредоточиться на сути программы, а не на многократном переписывании кода.
  • Инструменты для избежания ошибок и поддержания стабильной работы программы. Благодаря тому, что детали реализации компонентов хранятся внутри этих компонентов, повышается степень сохранности данных и изначально не допускается возникновение некоторых ошибок.
  • Удобно для командной разработки. Каждый программист может работать над закрепленным за ним списком задач, при этом используя код своих коллег.

Недостатки:

  • Сложность подхода. Во-первых, нужно обрести четкое понимание сути принципов и структурных элементов объектно-ориентированного программирования. Для этого необходимо изучить массу теории, примеров применения и много практиковаться. Во-вторых, важно научиться понимать, когда стоит прибегать к ООП, а когда это лишь ухудшает код: делает его чрезмерно сложным, громоздким и неэффективным.
  • Сниженная производительность. В сравнении с некоторыми другими подходами такие программы могут исполняться медленнее из-за потребления большого количества оперативной памяти.
  • Длинный код. Так происходит из-за необходимости создавать конструкции, которые включают все структурные элементы, описанные в этой статье. Этот недостаток указывает на то, что объектно-ориентированное программирование стоит применять не к каждой программе.

Языки объектно-ориентированного программирования

Так как объектно-ориентированное программирование — это подход к написанию кода, задействован может быть любой язык. И все же, есть языки, при работе с которыми наиболее удобно применять ООП:

  • Java — язык, который подходит для разработки различных приложений;
  • JavaScript — основной язык для веб-разработки;
  • Python — язык с широким списком применения: анализ данных, бэкенд-разработка и прочее;
  • C++ — написание различных программ: игры, ОС и другие;
  • C# — один из самых универсальных языков;
  • PHP — применяется в веб-разработке;
  • Ruby — этот язык часто используется в бэкенд-разработке веб-приложений;
  • Kotlin — подходит для разработки Android-приложений.

Это лишь наиболее популярные языки, которые поддерживают объекты и классы, что делает их подходящими для разработки по принципам ООП.