Объектно-ориентированный является очень броской фразой. Называя что-либо объектно-ориентированным, вы можете звучать весьма умно. Ruby позиционирует себя как объектно-ориентированный язык сценариев, но что на самом деле означает "объектно-ориентированный"?
Имеется множество вариантов ответов на этот вопрос, все из которых, вероятно, сводятся к одному и тому же. Вместо того, чтобы быстро резюмировать ответ, давайте подумаем немного о традиционной парадигме программирования.
Традиционно, проблема программирования подвергается подходу с некоторыми видами представления данных и процедур, которые оперируют этими данным. В данной модели, данные являются инертными, пассивными и беспомощными. Они сидят полностью во власти некоего большого процедурного тела, которое активно, логично и всемогуще.
Проблема такого подхода заключается в том, что такие программы пишутся программистами, которые всего лишь люди и могут сохранять достаточно ясно детали в своих головах до некоторого момента. Пока проект не станет большим, и его процедурное ядро не вырастет до той точки, с которой помнить обо всех вещах не станет достаточно трудно. Незначительные провалы в памяти и типографические ошибки в результате приводят к хорошо скрытым багам. Сложные и непредусмотренные действия начинают появляться в процедурном ядре, и разработка его становится похожим на попытку переноса гневного кальмара так, чтобы он не мог дотронуться до вашего лица. Имеются руководства по программированию, чтобы помочь минимизировать и локализовать баги традиционной парадигмы, но имеется лучшее решение, которое включает фундаментальные изменения, с которыми мы работаем.
Что даёт объектно-ориентированное программирование? Оно позволяет нам делегировать большую часть муторной и повторяющейся работы в сами данные. Это изменяет концепцию данных с пассивных к активным. Перефразируем:
- Мы прекращаем обработку каждой части данных в качестве открытой коробки, из которой мы достаём какие-то вещи и помещаем в неё какие-то вещи.
- Мы начинаем работать с каждой частью данных в качестве рабочей машины, которая закрыта и имеет ряд переключателей и циферблатов.
То, что описано выше в качестве машины, может быть очень простым снаружи и сложным внутри; Мы не можем сказать о ней что-то будучи снаружи, и мы не позволяем себе открыть эту машину (за исключением тех случаев, когда уверены в том, что внутри что-то не так), поэтому мы должны делать лишь те вещи, которые позволяют делать переключатели и циферблаты, чтобы взаимодействовать с данными. После того, как эта машина построена, мы уже не хотим думать о том, как она работает внутри.
Мы можем думать, что просто делаем больше работы для себя, но это имеет тенденцию хорошей работы по предотвращению всех видов ошибочных явлений.
Давайте начнём с примера, который слишком прост, чтобы быть практическим, но который должен иллюстрировать по крайней мере часть концепции. Ваш автомобиль имеет счётчик пути. Его работа заключается в сохранении дистанции пути пройденного машиной с последнего раза его обнуления. Как бы мы его написали на языке программирования? В C счётчик пути будет просто числовой переменной, возможно типа float . Программа будет управлять переменной постепенно увеличивая значение, со случайным возвратом к нулю при необходимости. Что здесь не так? Баг в программе может произойти при присвоении переменной ошибочного значения, при огромном множестве причин. Всякий, кто программировал на C, знает, что такое потратить несколько часов или дней на поиск такого бага, который будучи найденным, оказывается до абсурда простым. (Момент нахождения этой ошибки обычно обозначается звук громким хлопком по лбу)
Та же проблема будет рассматриваться под иным углом зрения в объектно-ориентированном подходе. Первая вещь, о которой программист спрашивает, когда разрабатывает счётчик пути, это не "какой тип данных наиболее подходит для описания данной вещи?", а "как именно должна работать данная вещь?". Различие здесь достаточно велико. Требуется потратить немного больше времени, решая как именно счётчик пути должен взаимодействовать с внешним миром. Мы решили спроектировать маленькую машину с управлением, которое позволяет увеличивать счётчик, сбрасывать его, считывать его значение и ничего больше.
Мы не предоставляем способов присвоения произвольного значения счётчик. Почему? Потому, что мы знаем, что счётчик не должен так работать. Имеется только несколько вещей, которые вы можете сделать со счётчиком, они все разрешены. Таким образом, если что-то еще в программе ошибочно попытается поместить другое значение (скажем, температура климат контроля в автомобиле) и счётчик, это немедленно скажет нам о том, что что-то пошло не так. На говорят, что при выполнении программы (или возможно во время компиляции, в зависимости от природы языка) нам не позволяется присвоить значение объекту счётчика пути. Сообщение не может быть точным, но этого будет достаточно, чтобы понять ошибку. Не так ли? Но это быстро указывает нам в направлении причины сбоя. Это лишь один из нескольких способов, которые в объектно-ориентированном программировании могут сэкономить время.
Мы обычно берём один шаг абстракции в данном случаем, потому как легко построить завод, который производит машины, которые делают машины с индивидуальным дизайном. Мы не можем создать один таймер для всего, но можем создать множество таймеров из одного шаблона. Шаблон (или если вам нравится, фабрика счётчиков) соответствует тому, что мы называем классом, а индивидуальный счётчик создаётся из этого шаблона (или создаётся фабрикой) соответствует объекту. Большинство объектно-ориентированных языков требуют, чтобы класс был определён до того, как будет создан объект, но Ruby так не делает.
Стоит отметить, что использование объектно-ориентированного языка не означает соблюдения правильного ООП дизайна. Действительно, на любом языке возможно написать код, который будет грязным, неаккуратным, неясным, забагованным и шатким во всём. Что Ruby делает для Вас (в противовес, особенно, C++). Он делает практику объектно-ориентированного программирования естественной по ощущениям, даже когда вы работаете в малом масштабе. Вы не чувствуете необходимость прибегать прибегать к уродливому коду, чтобы сэкономить усилия. Мы будем обсуждать способы, в которых Ruby выполняет эту замечательную цель в процессе изучения данного руководства. В следующей главе будут "переключатели и циферблаты" (методы объектов) и от них мы перейдём к "фабрикам" (классам). Вы всё ещё с нами?