Перейти к основному содержимому

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

Разработчику Архитектору
Сначала — общие понятия (раздел 4 "Код")

Если ООП для вас новое или вы учите Ruby с нуля, сначала пройдите материалы без привязки к синтаксису: парадигмы и уровни абстракции, затем ООП — о разделе — зачем объекты, введение, абстракция, инкапсуляция, наследование, полиморфизм.

Ниже — как это устроено в Ruby.

Теория и модель Ruby​

Ruby близок к Smalltalk (ООП в Smalltalk): всё значение — объект, взаимодействие через сообщения (obj.method — отправка сообщения). Класс — объект класса Class.

Понятие ООПКак выражено в Ruby
Класс, АДТclass, открытые классы (monkey patching)
Инкапсуляцияprivate / protected; сокрытие через соглашения (инкапсуляция)
Наследование< Parent; модули (include, prepend) вместо множественного наследования классов
Полиморфизмутиная типизация (duck typing) — важны методы объекта, а не имя класса; единый интерфейс сообщений
Сообщенияsend, method_missing; близко к модели Кэя (полиморфизм)

Определения — раздел 4-08-oop.

Кратко для новичка:

  • Класс — шаблон; Class.new создаёт объект и вызывает initialize.
  • @name — переменная экземпляра, видна только внутри методов этого объекта.
  • attr_accessor — автоматические геттер и сеттер для атрибута.
  • Наследование — оператор <; super вызывает метод родителя.
  • Модули (include) добавляют методы без второго родительского класса.
  • Утиная типизация — подходит любой объект с нужным методом, без объявления интерфейса.

ООП в Ruby​

КЛАСС Кот
поля: имя, возраст
метод мяукнуть()
КОНЕЦ

объект barsik := новый Кот(имя="Барсик", возраст=3)
barsik.мяукнуть()

Разбор:

  • Блок показывает идею класса как шаблона: в нём описываются поля и методы будущих объектов.
  • Строка новый Кот(...) соответствует созданию экземпляра через конструктор.
  • Вызов barsik.мяукнуть() иллюстрирует отправку сообщения объекту, то есть вызов его метода.
  • На этом уровне важна модель мышления: сначала описывается тип сущности, потом создаются конкретные объекты.

Порядок изучения ООП в Ruby​

  1. Класс и объект.
  2. Инкапсуляция и методы доступа.
  3. Наследование и super.
  4. Полиморфизм и утиная типизация.
  5. Модули и миксины.

Такой порядок ведёт от простых сущностей к расширяемой архитектуре без "теории ради теории".


Создание экземпляра через new, initialize и фабрики​

В Ruby отдельного синтаксиса constructor нет. Цепочка создания объекта:

  1. Вызывается Class.new(...) — метод класса (объекта Class).
  2. new выделяет память под объект и вызывает initialize с теми же аргументами.
  3. initialize возвращаемое значение игнорируется; наружу уходит объект из new.
class Order
def self.create_draft(customer_id)
new(customer_id, status: :draft)
end

def initialize(customer_id, status: :pending)
@customer_id = customer_id
@status = status
@lines = []
end
end

order = Order.create_draft(42)

Разбор:

  • self.create_draft — метод класса (фабрика); удобно скрывать сложную инициализацию.
  • Именованные аргументы (status:) — идиома Ruby 2+; читаемость как у keyword args в Python.
  • @lines = [] в initialize — у каждого заказа свой массив; не пишите @@lines без необходимости.

Запись в переменные, ссылки и self​

a = Person.new("Анна", 30)
b = a # та же ссылка на объект
b.instance_variable_set(:@name, "Борис")
a.name # => "Борис" — объект один

class Counter
def inc
@n = (@n || 0) + 1
self # явный возврат self для цепочки
end
end

Разбор:

  • Переменные a, b хранят ссылки; два имени — один объект (как в JS/Python).
  • self в методе — текущий объект; нужен, если присваиваете сеттеру: self.name = x (иначе локальная переменная).
  • Прямой доступ к @name снаружи невозможен без send / reflection — базовая инкапсуляция полей.

Класс как объект и иерархия Object​

Person.superclass # => Object
Person.ancestors # цепочка для поиска метода
5.class # => Integer
Integer.class # => Class
Class.class # => Class

Разбор:

  • Поиск метода идёт по ancestors (класс → модули → суперкласс → … → Object → Kernel).
  • Число 5 — полноценный объект; 5.times { ... } — сообщение целому числу (влияние Smalltalk).

Модификаторы доступа в Ruby​

МодификаторКто может вызватьТипичное применение
publicлюбой код (по умолчанию)API объекта
protectedself, наследники, другие экземпляры того же классасравнение other.balance внутри класса
privateтолько неявный получатель (вызов other.private_method запрещён)внутренние шаги алгоритма
class Account
def transfer(to, amount)
withdraw(amount)
to.deposit(amount) # deposit protected — OK для другого Account
end

protected

def deposit(amount)
@balance += amount
end
end

Разбор:

  • protected в Ruby шире, чем в Java: важен класс получателя, не только self.
  • private вызывают без явного получателя — только private_method, не obj.private_method.

Сообщения через send, method_missing и утиную типизацию​

obj.send(:greet) # то же, что obj.greet — динамическое имя
obj.respond_to?(:save) # есть ли метод?

class Flexible
def method_missing(name, *args)
puts "Неизвестное сообщение: #{name}"
end
end

Разбор:

  • send / public_send — отправка сообщения по имени во время выполнения (метапрограммирование, тестовые дублёры).
  • method_missing — перехват неизвестного сообщения (прокси, ORM); злоупотребление усложняет отладку.
  • Утиная типизация (duck typing) — объект подходит, если реализует нужные методы, без общего базового класса.

Сравнение Ruby с Java и Smalltalk​

ТемаRubyJavaSmalltalk
Вызовсообщение obj.mвызов методасообщение
Множественное наследование классовнетнетнет
Примесиinclude / prependинтерфейсы + defaulttraits (Pharo)
Поля@ivar скрытыprivate + accessorsinstance vars скрыты
Типыдинамическиестатическиединамические
Классобъект Classописание в метаданныхобъект метакласса

Пример класса​

Справочно на Ruby

Код ITЗагрузка примера кода…

Разбор:

  • class Unit ... end задаёт класс, объединяющий состояние персонажа и его поведение.
  • attr_accessor — сокращение, которое автоматически создаёт геттеры и сеттеры для перечисленных атрибутов.
  • initialize заполняет стартовые значения переменных экземпляра (@name, @health, @mana и т.д.).
  • damage вычисляет урон на основе характеристик и уровня.
  • attack(target) меняет состояние другого объекта через публичный интерфейс (инкапсуляция).
  • Unit.new создаёт два независимых объекта (warrior, mage) с разными параметрами.

Ключевое слово class открывает определение класса (имя с заглавной буквы, стиль CamelCase). Тело завершается end. Переменные экземпляра помечаются @; снаружи к ним обращаются через геттеры и сеттеры. Метод initialize вызывается автоматически при Unit.new и задаёт начальное состояние. Методы возвращают значение последнего выражения без обязательного return. Строковая интерполяция #{} работает в двойных кавычках.


Интерактивная схема — класс и объект (псевдокод, подходит для любого ООП-языка). Полный разбор принципов: ООП в разделе "Код и разработка".

Play ITЗагрузка интерактивного демо…


Классы и объекты​

Определение класса​

Класс в языке Ruby представляет собой шаблон или конструктор для создания объектов. Класс определяет структуру данных и поведение объектов, которые будут созданы на его основе. Для определения класса используется ключевое слово class, за которым следует имя класса.

class Person
def initialize(name, age)
@name = name
@age = age
end

def greet
puts "Привет, меня зовут #{@name}"
end
end

Разбор:

  • initialize(name, age) принимает входные параметры и сохраняет их в переменные экземпляра.
  • Символ @ обозначает поля конкретного объекта, доступные внутри его методов.
  • Метод greet формирует и выводит приветственную строку с интерполяцией #{@name}.
  • Этот пример показывает минимальный жизненный цикл — определить класс, сохранить состояние, вызвать поведение.

Имя класса в Ruby начинается с заглавной буквы (CamelCase). Каждый класс наследует от Object (наследование), что обеспечивает единообразие объектной системы.


Создание объектов​

Объекты создаются путем вызова метода new на классе. Этот метод создает новый экземпляр класса и вызывает конструктор initialize для настройки начального состояния объекта.

person1 = Person.new("Алексей", 30)
person2 = Person.new("Мария", 25)

person1.greet # Вывод: Привет, меня зовут Алексей
person2.greet # Вывод: Привет, меня зовут Мария

Каждый созданный объект является уникальным экземпляром класса и имеет собственное состояние. Объекты могут взаимодействовать друг с другом через вызов методов и обмен данными.


Мини-сниппет — композиция вместо наследования​

Код ITЗагрузка примера кода…

Разбор:

  • Engine отвечает только за свою роль — запуск двигателя.
  • Car получает Engine через конструктор, а не наследуется от него.
  • Такой подход называется композицией: объект собирается из зависимостей.
  • engine = Engine.new в параметре задаёт удобное значение по умолчанию.
  • Шаблон упрощает тестирование: в тесте можно передать фейковый объект двигателя.

Переменные экземпляра​

Переменные экземпляра в языке Ruby обозначаются символом @ в начале имени. Эти переменные принадлежат конкретному объекту и хранят его состояние. Каждый экземпляр класса имеет собственные копии переменных экземпляра.

Код ITЗагрузка примера кода…

Переменные экземпляра доступны во всех методах экземпляра класса и сохраняют свои значения между вызовами методов.


Методы экземпляра​

Методы экземпляра определяют поведение объектов. Они вызываются на конкретных экземплярах класса и имеют доступ к переменным экземпляра.

Код ITЗагрузка примера кода…

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


Инициализация объектов​

Метод initialize является конструктором класса. Он вызывается автоматически при создании нового объекта с помощью new. Этот метод устанавливает начальное состояние объекта.

class User
def initialize(username, email, age = 18)
@username = username
@email = email
@age = age
@created_at = Time.now
@active = true
end
end

user1 = User.new("john_doe", "john@example.com")
user2 = User.new("jane_smith", "jane@example.com", 25)

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


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

Интерактивная схема — инкапсуляция (псевдокод). Подробнее: Инкапсуляция.

Play ITЗагрузка интерактивного демо…


Переменные экземпляра и их защита​

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

Код ITЗагрузка примера кода…

Прямой доступ к переменным @temperature, @calibration_factor и @last_reading_time извне объекта невозможен. Это обеспечивает контроль над изменением состояния объекта.


Методы доступа​

Для доступа к переменным экземпляра извне используют методы доступа:

  • геттеры возвращают значения;
  • сеттеры устанавливают новые значения и могут проверять входные данные.

Код ITЗагрузка примера кода…

Методы доступа позволяют добавлять логику валидации и обработки при чтении и записи значений.


Сокращения для методов доступа​

Язык предоставляет специальные методы для автоматической генерации методов доступа: attr_reader, attr_writer и attr_accessor.

Код ITЗагрузка примера кода…

Язык предоставляет сокращения для генерации методов доступа:

  • attr_reader — только геттеры;
  • attr_writer — только сеттеры;
  • attr_accessor — геттеры и сеттеры для указанных атрибутов.

Модификаторы доступа​

Язык предоставляет три уровня доступа для методов: public, protected и private.

Код ITЗагрузка примера кода…

Язык предоставляет три уровня доступа для методов:

  • public — API объекта (по умолчанию);
  • protected — внутри класса и для экземпляров того же класса;
  • private — только внутри класса, без явного получателя.

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

Интерактивная схема — наследование (псевдокод). Подробнее: Наследование.

Play ITЗагрузка интерактивного демо…


Одиночное наследование​

Язык поддерживает одиночное наследование, где класс может наследовать от одного родительского класса. Для указания наследования используется оператор <.

Код ITЗагрузка примера кода…

Дочерние классы наследуют все методы родительского класса и могут добавлять новые методы или переопределять существующие.


Переопределение методов​

Дочерние классы могут переопределять методы родительского класса, предоставляя собственную реализацию.

Код ITЗагрузка примера кода…

Переопределение методов позволяет создавать специализированные реализации для разных типов объектов.


Метод super​

Метод super позволяет вызывать метод родительского класса из переопределенного метода дочернего класса.

Код ITЗагрузка примера кода…

Метод super может вызываться без аргументов, с теми же аргументами, что и текущий метод, или с указанием конкретных аргументов.


Иерархия классов​

Создание многоуровневых иерархий классов позволяет организовать код в логическую структуру.

Код ITЗагрузка примера кода…

Иерархия классов позволяет повторно использовать код и создавать специализированные классы на основе общих концепций.


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

Интерактивная схема — полиморфизм (псевдокод). Подробнее: Полиморфизм.

Play ITЗагрузка интерактивного демо…


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

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

Код ITЗагрузка примера кода…

Полиморфизм через наследование позволяет писать код, который работает с объектами разных типов через общий интерфейс.


Утиная типизация​

Утиная типизация (duck typing) — в Ruby важны методы объекта, а не его конкретный тип. Если объект реализует нужные методы, его можно использовать в соответствующем контексте.

Код ITЗагрузка примера кода…

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


Полиморфизм в коллекциях​

Полиморфизм особенно полезен при работе с коллекциями объектов разных типов.

Код ITЗагрузка примера кода…

Полиморфизм позволяет обрабатывать разнородные объекты единообразно через общий интерфейс.


Модули и миксины​

Определение модулей​

Модули в языке Ruby представляют собой коллекции методов и констант. Они не могут создавать экземпляры, но могут включаться в классы для добавления функциональности.

Код ITЗагрузка примера кода…

Модули группируют связанные методы и могут использоваться для организации кода и повторного использования функциональности.


Включение модулей​

Модули включаются в классы с помощью ключевого слова include. Методы модуля становятся методами экземпляра класса.

Код ITЗагрузка примера кода…

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


Расширение классов​

Ключевое слово extend добавляет методы модуля как методы класса, а не методы экземпляра.

Код ITЗагрузка примера кода…

extend добавляет методы модуля на уровне класса, делая их доступными без создания экземпляра.


Комбинированное использование модулей​

Модули могут включаться и расширять классы одновременно, обеспечивая гибкость в организации кода.

Код ITЗагрузка примера кода…

Комбинированное использование модулей позволяет создавать мощные и гибкие системы повторного использования кода.


Особенности объектно-ориентированного программирования в языке Ruby​

Всё есть объект​

В языке каждое значение является объектом, включая числа, строки, символы и даже классы.

Код ITЗагрузка примера кода…

Объектная природа всех значений обеспечивает единообразие и мощь языка.


Динамическая природа языка​

Язык позволяет изменять классы и объекты во время выполнения программы.

Код ITЗагрузка примера кода…

Динамическая природа языка позволяет создавать гибкие и адаптируемые программы.


Метапрограммирование​

Метапрограммирование позволяет программам создавать и изменять другие программы или сами себя во время выполнения.

Код ITЗагрузка примера кода…

Метапрограммирование позволяет создавать мощные абстракции и уменьшать повторение кода.


Методы доступа и атрибуты​

Расширенные возможности атрибутов​

Методы attr_reader, attr_writer и attr_accessor могут использоваться с дополнительными возможностями.

Код ITЗагрузка примера кода…

Расширенные возможности атрибутов позволяют создавать более умные и безопасные интерфейсы доступа к данным.


Практические ошибки в ООП-коде Ruby​

Слишком "толстые" модели​

Большие классы с десятками обязанностей быстро становятся дорогими в поддержке. Рабочий подход:

  • оставить в модели состояние и базовые инварианты;
  • вынести сложные сценарии в сервисные объекты;
  • выделить интеграции с внешними API в отдельные адаптеры.

Злоупотребление метапрограммированием​

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


Как внедрять ООП-подход по шагам​

Для учебных и рабочих проектов удобно двигаться так:

  1. Сначала описать сущности и их состояние через простые классы.
  2. Затем добавить минимальные публичные методы и базовую валидацию.
  3. После этого выделить общие части в модули.
  4. И только на последнем шаге подключать метапрограммирование, когда повторяемость действительно заметна.

Так код остаётся понятным на каждом этапе роста проекта.


Защита данных через приватные методы доступа​

Приватные методы доступа обеспечивают дополнительный уровень защиты данных объекта.

Код ITЗагрузка примера кода…

Приватные методы доступа предотвращают прямое обращение к чувствительным данным извне объекта.


Исторический контекст объектно-ориентированного программирования в языке Ruby​

Развитие концепций​

Язык был создан Юкихиро Мацумото в 1995 году с целью объединить лучшие черты различных языков программирования. Концепция объектно-ориентированного программирования в языке была вдохновлена языком Smalltalk, который считается одним из первых чисто объектно-ориентированных языков.

# Влияние Smalltalk видно в том, что всё является объектом
5.times { puts "Hello" } # 5 - это объект класса Integer
"hello".upcase # "hello" - это объект класса String

# Влияние Perl видно в гибкости синтаксиса
array = [1, 2, 3]
array.each { |x| puts x } # Блоки кода как замыкания

# Влияние Python видно в читаемости кода
def calculate_area(width, height)
width * height
end

Первая стабильная версия языка 1.0 была выпущена в 1996 году. Версия 1.8, выпущенная в 2003 году, стала широко используемой и ввела множество улучшений в объектную модель.


Эволюция языка​

Версия 1.9, выпущенная в 2007 году, принесла значительные изменения в объектную модель, включая новую систему кодирования строк и улучшенную производительность.

Код ITЗагрузка примера кода…

Версия 2.0, выпущенная в 2013 году, добавила ключевые слова в методах и другие улучшения. Версия 3.0, выпущенная в 2020 году, принесла значительные улучшения производительности и новые возможности языка.


Влияние на современную разработку​

Язык оказал значительное влияние на современную веб-разработку через фреймворк Ruby on Rails, выпущенный в 2004 году. Rails популяризировал концепции соглашения над конфигурацией и принцип "не повторяйся".

Код ITЗагрузка примера кода…

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


Современное состояние​

Современная версия языка 3.2 (на момент 2026 года) продолжает развивать объектно-ориентированные концепции, добавляя новые возможности и улучшая производительность.

Код ITЗагрузка примера кода…

Язык продолжает развиваться, сохраняя при этом свою философию простоты и выразительности.


Связанные статьи энциклопедии​