Modules & Mixins

Модули Ruby - одна из самых мощных возможностей языка для организации кода и совместного использования функциональности между классами. В отличие от классов, модули нельзя создать напрямую, но они предоставляют важные механизмы для пространства имен, повторного использования кода и реализации множественного наследования с помощью примесей (mixin). Модульная система Ruby предоставляет элегантные решения для совместного использования поведения между несвязанными классами, сохраняя при этом организованность кода.

К примеру, демонстрация основной концепции mixin -

module Greetings
def say_hello
puts "Hello from module!"
end

def say_goodbye
puts "Goodbye from module!"
end
end

class Person
include Greetings
end

person = Person.new
person.say_hello # => Hello from module!
person.say_goodbye # => Goodbye from module!


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

module Animals
class Dog
def speak
puts "Woof!"
end
end

class Cat
def speak
puts "Meow!"
end
end
end

module Robots
class Dog
def speak
puts "Beep beep!"
end
end
end

# Access classes through their namespaces
animal_dog = Animals::Dog.new
robot_dog = Robots::Dog.new

animal_dog.speak # => Woof!
robot_dog.speak # => Beep beep!

# You can also access them this way
cat = Animals::Cat.new
cat.speak # => Meow!

Оператор разрешения области видимости :: позволяет точно указать, какой именно класс Dog необходимо использовать. Это устраняет неоднозначность и позволяет иметь несколько классов с одинаковым именем в разных контекстах.


В Ruby существует три различных способа включения модулей в классы, каждый из которых служит разным целям: include, extend, и prepend.

Функция include добавляет методы к экземплярам класса(инстансные), а extend к самому классу -

module Speakable
def greet
puts "Hello from #{self.class}"
end
end

class Person
include Speakable
end

class Robot
extend Speakable
end

# include makes module methods available to instances
person = Person.new
person.greet

# extend makes module methods available to the class itself
Robot.greet

# This won't work - Robot instances don't have access
# robot = Robot.new
# robot.greet # This would raise an error


Функция prepend похож на include, но изменяет порядок поиска методов, что позволяет создавать мощные шаблоны оберток. Благодаря использованию префикса prepend, метод модуля вызывается первым и может вызывать метод класса через super. Это создает удобную структуру обертки, где модуль может добавлять поведение до и после основной функциональности.

module Loggable
def process_data
puts "Logging: Starting data processing"
super
puts "Logging: Finished data processing"
end
end

class DataProcessor
prepend Loggable

def process_data
puts "Processing the actual data"
end
end

class SimpleProcessor
include Loggable

def process_data
puts "Simple processing"
super rescue puts "No super method found"
end
end


puts "With prepend:"
DataProcessor.new.process_data
# => With prepend:
# => Logging: Starting data processing
# => Processing the actual data
# => Logging: Finished data processing

puts "With include:"
SimpleProcessor.new.process_data
# => With include:
# => Simple processing
# => Logging: Starting data processing
# => No super method found