Рубріки: ОсновыТеория

Объектно-ориентированное программирование (ООП). Объясняем на пальцах

Денис Бородовский

Объектно-ориентированное программирование (в дальнейшем ООП) — парадигма программирования, в которой основными концепциями являются понятия объектов и классов. ООП создано для моделирования алгоритмов, бизнес-процессов или любой иной формализованной логики. Проще говоря, ООП разработано для упрощения программирования комплексных программных продуктов. Далее попробуем разобраться с концепцией объектно-ориентированного программирования на простых примерах.


1. Что такое объектно-ориентированное программирование?

Система объектно-ориентированного программирования (ООП) — это парадигма основанная на концепции «объектов», содержащих данные и методы.

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

2.1. Что такое объект?

Объект — любой предмет или сущность, имеющий состояние и поведение.

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

Пример:

  1. Объект: дом
  2. Состояние: адрес, цвет, площадь
  3. Поведение: открыть дверь, закрыть дверь

Вот так реализация этого объекта будет выглядеть на объектно-ориентированном языке Java:

class House {
   String address;
   String color;
   double are;
   void openDoor() {
      //здесь пишется код
   }
   void closeDoor() {
      //место для вашего кода
   }
 ...
 ...
}

Различные состояния объекта представлены в виде переменных экземпляра, а поведение — это методы класса.

Помогут разобраться в этом вопросе курсы от наших друзей Mate Academy, Hillel и Powercode. Менторы смогут ответить на любые ваши вопросы.

2.2. Что такое класс?

Класс можно рассматривать как план, с помощью которого можно создать столько объектов, сколько захотите.

Возьмем класс Site с двумя элементами данных поля или переменные экземпляра и состояние объекта. Это просто план, он не принадлежит какому-то отдельному сайту, однако с его помощью мы можем создавать объекты (или экземпляры), представляющие любой из них.

В следующем примере мы создали два объекта и предоставили им отдельные свойства с помощью конструктора:

(Продолжаем использовать Java)

public class Site {
   //поля (или переменные)
   String webName;
   int webAge;

   // конструктор
  Site(String name, int age){
      this.webName = name;
      this.webAge = age;
   }
   public static void main(String args[]){
      //создаем объекты
      Site obj1 = new Site("highload", 5);
      Site obj2 = new Site("yandex", 18);

     //Получаем данные объекта и выводим их в консоль
     System.out.println(obj1.webName+" "+obj1.webAge);
     System.out.println(obj2.webName+" "+obj2.webAge);
   }
}

3. Что не так с процедурным программированием (ПП)?

До ООП все языки программирования были процедурными. Назывались они так потому, что программист определял очень специфический набор процедур (подпрограмм), которые должен был выполнять компьютер. Это пошаговое руководство включало в себя прием данных, выполнение последовательности действий с этими данными, а затем вывод того, что получилось в результате этих действий.

Процедурные языки хорошо работали в те времена а некоторые до сих пор используются. Однако ряд существенных недостатков у них все же есть. В основном, сложности появлялись при желании программиста сделать что-то, не входящее в базовую последовательность шагов (доработка и поддержка софта), а также при создании больших программ (сложный комплексный софт). Иногда получалось такое спагетти, которое трудно было распутать.

Также разработчиками отмечались следующие недостатки ПП:

  1. Огромное количество процедур при разработке объемных проектов сказывается на чистоте и работоспособности кода.
  2. Раздутые библиотеки кода, где содержаться сотни подпрограмм — очень трудно поддерживать.
  3. Из-за непрозрачной структуры библиотек, существует возможность одним «неловким движением» испортить всю библиотеку (это реально бывает).
  4. Все данные процедуры доступны только внутри нее. Отсутствует возможность вызвать их из другого места, а также использовать повторно. Принцип DRY (Don’t Repeat Yourself) тут не работает.
  5. Монолитные большие куски кода довольно сложно модифицировать и расширить.

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

Далее рассмотрим ООП более подробно и поговорим о четырех китах, на которых оно стоит.

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

Абстракция

При использовании чего-либо (предмета или метода) — вам не обязательно знать, как он работает. Для примера возьмем кофе-машину, внутри которой довольно сложный механизм. Однако все что нам от нее надо — нажав кнопку с изображением дымящейся кружки — получить порцию ароматного эспрессо.

То же самое верно и для объектов в ООП. В любом встроенном методе присутствует скрытый функционал. Например, в примере с объектом «Дом» есть метод openDoor(). Он может состоять из нескольких элементов: поворота ключа в дверном замке, дергания за ручку и т.д. Но нам с вами не обязательно об этом знать. Все, что нужно, прописать этот метод в редакторе кода и она откроется. Это абстракция.

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

Инкапсуляция — это один из способов создания абстракции. Каждый объект представляет собой набор данных (переменные, методы).

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

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

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

Для примера возьмем древнейшую игру — шахматы. У каждой шахматной фигуры есть свои классы с переменными и методами для передвижения и других действий. Более подробно рассмотрим пешку — обозначим ее классом Piece, поместив для нее внутри необходимый функционал. Однако помимо стандартных функций, пешкам еще нужен метод, превращающий их в другие фигуры, по достижении конца доски. Мы назовем его методом transformPiece().

Мы не будем помещать его в класс, а вместо этого создадим подкласс под названием Pawn. Поскольку это подкласс, он наследует все атрибуты от класса Piece. Экземпляр подкласса Pawn будет включать в себя не только метод transformPiece(), но и базовые свойства и атрибуты класса-родителя (цвет, высоту, форму и разрешенное движение).

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

Полиморфизм

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

Например, наш класс Piece может иметь метод move(), перемещающий фигуру на одну позицию в любом направлении. Такая функция будет работать и для фигуры короля, но не для чего-либо еще. Чтобы решить эту проблему, мы можем определить новый метод move() в подклассе Rook, определяющий движение как неограниченное количество пробелов вперед, назад, влево или вправо.

Теперь, когда программист вызывает метод move() и использует фигуру в качестве аргумента, программа будет точно знать, как она должна двигаться. Это экономит массу времени на попытки выяснить, какой из множества различных методов вы должны использовать. Это делает решение более общим и универсальным, расширяя базовую функциональность первоначального метода.

5. История появления ООП

Основы ООП зародились еще в 60-х годах XX века. Норвежцы Кристен Найгаард и и Оле-Йохан Даль разработали язык для создания симуляций и назвали его Simula 67. Основной задачей языка была симуляция взрыва кораблей различного назначения и модификаций.

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

А вот сам термин «объектно-ориентированное программирование» впервые был произнесен вслух в компании Xerox, при разработке языка программирования Smalltalk. Ввели его, чтобы обозначить процесс применения объектов — как основу для вычислений. Вдохновленные ​​проектом Simula 67, создатели Smalltalk сделали его динамичным.

В отличие от большинства статических систем того времени, с объектами можно было взаимодействовать на высоком уровне: изменять, создавать новые или удалять. Кроме того, Smalltalk был первым языком программирования, в котором была представлена ​​концепция наследования.

Simula стал вдохновляющим примером для большинства других языков, в том числе Pascal и Lisp, в 1980-х годах к ним присоединился еще и C++ (который стал образцом реализации современного ООП).

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

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

  • Легкий удобочитаемый код.
  • Быстро пишется.
  • Простая реализация большого функционала.
  • Не нужно писать однотипные функции для разных сущностей.
  • Меньше повторений.

Недостатки:

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

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

Вот несколько популярных языков, поддерживающих принцип ООП:

  1. Java.
  2. Python.
  3. C++.
  4. Ruby.
  5. C#.
  6. JavaScript.
  7. Objective-C.
  8. PHP.
  9. Perl.
  10. Scala.
  11. Swift.

Список, конечно же, неполный, на самом деле их гораздо больше.

8. Простой пример реализации ООП-концепции

Допустим, клиент заказал у вас сделать новый Тамагочи с белым медведем в качестве виртуального питомца. Вы принимаетесь за работу и создаете класс PolarBear на JavaScript/TypeScript.

class PolarBear {
 private _weight: number = 990;
 constructor(weight: number = 0) {
   this._weight = weight
 }
 makeNoise() {
   console.log("ревет");
 }
 eat() {
   console.log("ест все что захочет");
 }
 sleep() {
   console.log("спит");
 }
 roam() {
   console.log("бесцельно бродит);
 }
}

Затем заказчик попросил вас впихнуть в разрабатываемый гаджет всех существующих медведей, чтобы белому было немного веселей. Вы, закатывая рукава, возвращаетесь к работе и создаете копии класса медведей. Следующее желание клиента — добавить информацию о происхождении для каждого питомца. Вы больше не дублируете код.

Теперь вы меняете сотни строк кода для всех восьми видов медведей. Все это сопровождается вашими недовольными возгласами и неминуемым появлением кучи ошибок из-за множества правок. Пока вы тренируетесь, ваш клиент снова звонит и просит добавить в игру грызунов и жирафа. Вы понимаете, что когда вы закончите, он захочет еще обезьян и гиппопотама и вам нужен лучший способ все это внедрить.

Чтобы приручить свой виртуальный зверинец, вам поможет ООП, а именно простая структуризация кода: черные медведи, гризли и медведи-ленивцы — объединим в класс Медведи. В то же время медведи, грызуны и обезьяны — будут помечены классом Животные. Вот так мы постепенно и построим наше генеалогическое древо.

Это только часть кода:

class Animal {
 private _weight: number;
 private _origin: string;
 constructor(weight: number = 0, origin: string = "") {
   this._weight = weight;
   this._origin = origin;
 }
 makeNoise(noise: string = "") {
   console.log("издал звук, похожий на:  " + noise);
 }
 eat(food: string = "") {
   console.log("ест " + food);
 }
 sleep() {
   console.log("спит");
 }
 roam() {
   console.log("бесцельно бродит");
 }
}
class Bear extends Animal {
 constructor(weight: number, origin: string) {
   super(weight, origin);
 }
 makeNoise(noise: string = "рев") {
   super.makeNoise(noise);
 }
 eat(food: string = "все, что он хочет") {
   super.eat(food);
 }
}
class GrizzlyBear extends Bear {
 constructor(weight: number = 600, origin: string = "Северная Америка") {
   super(weight, origin);
 }
}
class Panda extends Bear {
 constructor(weight: number = 230, origin: string = "Китай") {
   super(weight, origin);
 }
 makeNoise() {
   super.makeNoise("писк");
 }
 eat() {
   super.eat("молодые побеги бамбука");
 }
}

Надеюсь, сам принцип понятен. А от теории, как известно, до реальной практики всего один шаг.

Коротко о главном

Что вам нужно знать об ООП:

  • Объектно-ориентированное программирование собирает информацию в отдельные сущности, называемые объектами.
  • Каждый объект является единственным экземпляром класса.
  • Абстракция скрывает внутреннюю работу объекта, когда ее не нужно видеть.
  • Инкапсуляция хранит связанные переменные и методы внутри объектов и защищает их.
  • Наследование позволяет подклассам использовать атрибуты родительских классов.
  • Полиморфизм позволяет объектам и методам работать в разных ситуациях с помощью одного интерфейса.

В конце, как обычно, несколько релевантных ссылок на актуальные видеоролики по теме:

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023