Объектно-ориентированное Программирование (ООП) в Ruby


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

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

Три кита объектно-ориентированного программирования: Инкапсуляция, Наследование, Полиморфизм
Для начала давайте создадим код, на котором мы будем разбирать примеры,он будет состоять из двух классов: Kitten и Cat, где Kitten наследуется от класса Cat

Что есть Инкупсуляция?
Инкапсуляция — это меньший кит объектно-ориентированного программирования. Инкапсуляция заключается в том, что присущие функциональному программирования данные и функции над ними превратились в свойства и методы объекта, которые заключены в объект.
Доступ к этим свойствам и методам доступен только через специально определяемые интерфейсы. Эти интерфейсы называются геттеры и сеттеры. Геттеры получают значение, а сеттеры устанавливают.

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

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

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

С помощью наших объектов покажем простой пример инкапскуляции
class Cat

def initialize(options={})
@height = options[:height]
@weight = options[:weight]
end

# Наш геттер для свойства рост
def height
@height
end

# Наш геттер для свойства вес
def weight
@weight
end

# Устанавливаем сеттер для свойства рос
def height=(height)
@height = height
end

def weight=(weight)
@weight = weight
end
end

tom = Cat.new({height: 10, weight: 100})
p tom.height
tom.height = 25
p tom.height


Здесь хороший совет, обязательно по экспериментируйте с геттерами и сеттерами. Посмотрите, что происходит если их убрать.
Метод initialize позволяет задать свойства объекту при его создании(при использовании метода new). Где свойства передаем как хеш значений.


Можно использовать упрошенное создание геттеров и сеттеров

attr_reader :height, :weight # Для геттера
attr_writer :height, :weight # Для сеттера
attr_accessor :height, :weight # Для геттера и сеттера


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

Как котенок произошел от кошки, так и один класс может наследоваться от другого.
Наследование реализуется очень просто, при определении класса Kitten достаточно дописать < Cat, что и указывает на наследование класса Kitten от класса Cat.

Говорят, что Cat наследует Kitten, а Kitten наследуется от Cat. Говорят, что Cat — супер класс или родительский класс, а Kitten — подкласс, субкласс или дочерний класс.

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

class Kitten < Cat

def initialize(options)
@name = options[:name]
super
end

def name
@name
end

def name=(name)
@name = name
end
end

p k = Kitten.new({height: 1, weight: 10, name: 'Tom'} )


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

Другим объектам при взаимодействии не важно какого класса объект(родитель или наследник) с которым они имеют взаимодействие. Важно, что он имеет тот интерфейс, который от него ожидают.

Все эти различия создает полиморфизм!
Объектно-ориентированное Программирование (ООП) в Ruby
Также ключевое слово super возвращает свойства из родительского класса. Мы сами определяем, какие свойства нужны наследнику, а какие нет!