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

Объектно-ориентированное программирование в 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 (класс → модули → суперкласс → … → ObjectKernel).
  • Число 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Загрузка примера кода…

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


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