Почему мы помещаем модуль внутри класса в Ruby?
В Ruby я вижу, что полезно использовать классы внутри модулей ради пространства имен. Я также вижу, что можно поместить модули внутри классов. Но я не понимаю, почему вы это сделаете.
Модули обычно смешиваются с классами, правильно? Итак, какова была бы цель определения модуля внутри класса?
Ответы
Ответ 1
Мы могли бы использовать его при написании подобного обезьяне кода:
class DrugDealer
module Drug
def happy?; true; end
end
def approach(victim)
victim.extend Drug
end
end
o = Object.new
DrugDealer.new.approach(o)
o.happy? # => true
Другим примером, который был бы более практичным в реальном мире, является наличие миксинов, которые применяются только подклассами.
Это полезно, когда некоторые грани предмета применяются к некоторым подклассам, а другие грани применяются к другим подклассам, если не существует достаточного порядка в том, как эти аспекты применяются, чтобы освободить место для ясной иерархии классов (дерева). Подумайте о множественном наследовании! Упрощенный пример:
class Person
def handshake
:sloppy
end
def mind_contents
:spam
end
module Proper
def handshake
:firm
end
end
module Clever
def mind_contents
:theories
end
end
end
class Professor < Person
include Proper
include Clever
# ...
end
И так далее. Вид приятный, когда используется разумно. Даже супер-вызовы и конструкторы (я не определял их здесь) протекают через все микшины и классы так, как я их хочу.
Ответ 2
class Image
module Colors
Red = ...
Blue = ...
end
include Colors
end
include Image::Colors
Image.new.set_pixel x, y, Red
Ответ 3
С тех пор я столкнулся с прецедентом в большом приложении Rails со сложным пространством имен. Упрощенный пример:
# app/models/invoice/dependents/item.rb
class Invoice
module Dependents
class Item
# Define invoice item
end
end
end
Здесь Invoice
- это собственный класс, но также является хорошим пространством имен для его зависимых элементов. Мы не можем сказать module Invoice
, потому что эта константа уже определена как класс, но мы все равно можем использовать ее как пространство имен.
Гигантский оговорка
Если вы используете класс как пространство имен и используете Rails, убедитесь, что не объявляете этот класс в другом месте. Автозагрузка погубит ваш день. Например:
# app/helpers/invoice/dependents/items_helper.rb
class Invoice # This line will cause you grief
module Dependents
module ItemsHelper
# view helper methods
end
end
end
Тот факт, что class Invoice
указан в этом файле, создает зависимость порядка загрузки; если эта строка class Invoice
выполняется до вашего предполагаемого определения класса, определение предполагаемого класса может не работать должным образом. В этом примере я не могу объявить, что Invoice
sublcasses ActiveRecord::Base
, если Invoice
уже объявлен без родительского класса.
Вы могли требовать, чтобы ваш "истинный" класс определял файл вверху другого файла, но, по крайней мере, в сценарии автозагрузки Rails, у вас будет меньше проблем, если вы сделаете это вместо этого:
# app/helpers/invoice/dependents/items_helper.rb
module Invoice:Dependents::ItemsHelper
# view helper methods
end
С помощью этого синтаксиса Rails увидит константу Invoice
и будет использовать autoload, чтобы найти ее, найти ее в файле модели и определить ее так, как вы планировали.
Ответ 4
Я предполагаю, что это действительно просто использование класса в качестве пространства имен, что иногда просто более удобно, чем помещение всего в модуль. Ive никогда не видел это на практике, но его вполне корректный Ruby-код в любом случае.
Единственный реальный сценарий, о котором я могу думать, - использовать EventMachine в классе:
class Api
def initialize
EM.start_server "0.0.0.0", 8080, Server
end
module Server
def receive_data (data)
# do stuff
end
end
end