Объектно-ориентированное программирование: инкапсуляция, полиморфизм, наследование

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

Чтобы успешно реализовать объектно-ориентированный дизайн, нужно тщательно и интуитивно проектировать вокруг 4 основных столпов, известных как 4 столпа объектно-ориентированного дизайна:

  • Абстракция
  • Наследование
  • Инкапсуляция
  • Полиморфизм

Абстракция:

Абстракция и инкапсуляция - очень похожие объектно-ориентированные концепции, но отличаются в нескольких ключевых областях. Абстракция позволяет инженерам мыслить на более высоком уровне, получая "взгляд с высоты птичьего полета" на программу, не увязая в лежащих в основе механиках. Эта перспектива «более высокого уровня» называется уровнем разработки. В отличие от инкапсуляции, абстракция имеет тенденцию обобщать вещи, скрывая нежелательные данные и выявляя необходимые данные. С другой стороны, инкапсуляция фокусируется на внутренних механизмах действия программы и сокрытии этих механизмов (состояний / поведения) с помощью модификаторов доступа.

Пример использования абстракции - в языках программирования. Первоначально Launch School использовала Ruby, потому что он был предназначен для англоговорящего человека, а не для компьютера. Написание на Ruby больше похоже на письмо на английском, и многие лежащие в его основе механизмы скрыты. Это достигается благодаря высокому уровню абстракции в Ruby. Языки нижнего уровня не содержат большой абстракции, поэтому от программиста требуется учитывать большее количество базовых атрибутов уровня реализации, чтобы код работал.

Design-Level: High-level, 'birds eye' view of a program. The realm of abstraction, responsible for getting rid of unnecessary data that doesn't relate to the overall design of a program.
Implementation-Level: Mechanics-layer. The data, states, and behavior that contribute to the functionality of a program. The realm of encapsulation that shields unnecessary data to the public interface.

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

В зависимости от языка программирования существует множество различных типов наследования. Ruby, однако, допускает только единичное наследование между классами и использует то, что называется миксинами, когда подклассы к .

Single Inheritance: Sub-class/child class inherits from one super class.

Пример одиночного наследования в Ruby:

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

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

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

Другой фрейм для просмотра инкапсуляции - это «объединение». Инкапсуляция принимает разные классы, методы и переменные и объединяет их в единый блок для справки.

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

Инкапсуляция встречается во многих нишах Ruby, некоторые примеры находятся в:

  • Переменные
  • Методы
  • Классы

Переменные, в частности переменные экземпляра, можно инкапсулировать с помощью ключевых слов private и protected. Ключевое слово private скрывает данные из открытого интерфейса метода, что делает невозможным прямой доступ к ним извне класса. Ниже приведен пример инкапсуляции ключевым словом private в действии:

Пример инкапсуляции:

В приведенном выше коде создается единый блок, содержащий методы: класс Dog. После определения класса необходимо определить открытый интерфейс модуля. Метод конструктора initialize в строке 2 и метод экземпляра to_s в строке 6–8 являются частью открытого интерфейса класса Dog.

Public Interface:
  All the states and method’s available for direct-access outside of the Dog class. It is the 'logical point which outside classes, objects, and methods interact.'

Оба класса initialize и to_s доступны извне. Однако метод получения name, определенный для строки 12, скрыт извне класса по ключевому слову private. Это заключительный этап инкапсуляции, ограничивающий доступ к ненужным общедоступным данным внутри модуля.

При вызове метода получения name извнеDog запускается NoMethodError, поскольку в этой области недоступен метод. Кроме того, переменная экземпляра name скрыта извне класса, поэтому единственный способ получить доступ к имени собаки - использовать метод to_s.

Инкапсуляция с использованием ключевого слова protected аналогична private, за исключением того, что ключевое слово public позволяет осуществлять межклассовое взаимодействие между состояниями и поведениями, определенными под ним. Это полезно, когда вам нужны два класса для обмена информацией, но вы не хотите, чтобы эта информация ни в одном из общедоступных интерфейсов класса.

Инкапсуляция происходит во многих ключевых игроках Ruby. Например,

  • Классы: инкапсулируйте методы.
  • Методы: инкапсулируйте логику.
  • Объекты: инкапсулируют состояние и поведение.

Полиморфизм:

Короче говоря, полиморфизм - это способность данных быть представленными более чем одним способом. Рубисты обычно реализуют полиморфизм с помощью двух процессов:

  • Через наследование.
  • С помощью утиного ввода.

Полиморфизм через наследование:

До сих пор мы обсуждали концепцию одиночного наследования в Ruby. Однако есть еще одна форма наследования, которую использует ruby: наследование интерфейса. Наследование интерфейса использует mixins для включения модулей в класс, при этом каждый класс наследуется от интерфейса, предоставленного модулем.

Полиморфизм посредством утиного ввода:

Утиная печать основана на поговорке: «Если она ходит как утка и ведет себя как утка, значит, это должна быть утка». Основное внимание уделяется тому, что объект может делать, а не тому, что он равно. Согласно утиной типизации, нужно обрабатывать объекты в соответствии с определенными методами / поведением, а не унаследованными классами и смешанными модулями.

Пример утиного ввода:

В приведенном выше коде все 4 класса: Cat, Dog, Bird и Fish будут пытаться вызвать метод экземпляра make_noise. Однако в последнем элементе массива, объекте класса Fish, нет поведения для создания шума (рыба не издает шум). Таким образом возникает ошибка и происходит сбой кода.

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

Резюме:

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

Большое спасибо Карлу Лингия за поддержку и руководство в изучении этих столпов, а также за контент, представленный в этой статье, особенно в отношении основ инкапсуляции и абстракции.

Еще раз благодарим дружелюбный и услужливый персонал Launch School за то, что помог мне начать мои первые шаги на долгом пути от новичка до мастера.