Часто можно получить тот же результат: класс поддерживает методы класса, константы и пространство имён, поэтому экземпляр нужен не для каждого вызова. Но класс нельзя передать в include как обычный модуль. Модуль удобен для mixin-поведения; класс — для объектов и наследования. Альтернативой модулю могут быть наследование или композиция, но они меняют устройство и зависимости программы.
Можно ли с помощью класса реализовать ту же функциональность, что и в модуле?
Класс может предоставлять методы и пространство имён, но не является прямой заменой модуля, подключаемого через include или extend.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Да, если под функциональностью понимать результат работы: вычисление, форматирование или группировку методов. Но класс и модуль не взаимозаменяемы во всех механизмах Ruby.
Класс может содержать константы, вложенные определения и методы класса, вызываемые без создания его экземпляра. Поэтому утверждение «для вызова любого метода класса сначала нужен new» неверно. Сам класс тоже является объектом Ruby. Обычно классы выбирают, когда нужны экземпляры с собственным состоянием и поведением. Документация Class.
Модуль удобен как пространство имён и набор повторно используемого поведения — mixin. В примере один способ добавляет поведение экземпляру, а другой предоставляет самостоятельный метод класса:
module Greetings
def greet
'Привет!'
end
end
class Person
include Greetings
end
class Greeter
def self.greet
'Привет!'
end
end
Person.new.greet # => 'Привет!'
Greeter.greet # => 'Привет!'
include Greetings делает экземплярные методы модуля доступными экземплярам Person через цепочку поиска методов. Person.extend(Greetings) добавил бы их самому объекту класса, позволяя вызвать Person.greet. При этом собственные singleton-методы модуля не становятся автоматически методами включившего его класса. Документация Module.
Хотя Class наследуется от Module, передать обычный класс в include вместо модуля нельзя: это завершится TypeError. Можно вынести поведение в родительский класс, но наследование классов в Ruby одиночное. Другой вариант — композиция: объект хранит помощника и делегирует ему вызовы.
Итого: одинаковый внешний результат реализуем разными средствами, однако замена модуля классом может изменить интерфейс, наследование и зависимости. Если требуется разделить поведение между несвязанными классами, модуль часто выражает намерение точнее.