Подтвердить что ты не робот

Ruby module_function vs включая модуль

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

module MyModule
  def do_something
    puts "hello world"
  end
  module_function :do_something
end

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

Почему бы просто не

def MyModule.do_something

ИЛИ

def do_something

В каких случаях было бы полезно иметь доступную функцию для смешения или использования в качестве статического метода?

4b9b3361

Ответ 1

Подумайте о Enumerable.

Это прекрасный пример того, когда вам нужно включить его в модуль. Если ваш класс определяет #each, вы получаете много добра, просто включив модуль (#map, #select и т.д.). Это единственный случай, когда я использую модули как mixins - когда модуль предоставляет функциональность с точки зрения нескольких методов, определенных в классе, который вы включите в модуль. Я могу утверждать, что это должен быть единственный случай вообще.

Что касается определения "статических" методов, лучшим подходом будет:

module MyModule
  def self.do_something
  end
end

Вам действительно не нужно вызывать #module_function. Я думаю, что это просто странное наследие.

Вы можете даже сделать это:

module MyModule
  extend self

  def do_something
  end
end

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

Наконец, если вы просто выполните:

def do_something
end

... он не будет использоваться как глобальная функция, а как частный метод на Object (в Ruby нет функций, просто методов). Есть два недостатка. Во-первых, у вас нет пространства имен - если вы определяете другую функцию с тем же именем, она получает оценку, которую вы получите позже. Во-вторых, если у вас есть функциональность, реализованная в терминах #method_missing, использование частного метода в Object будет теневым. И, наконец, патч обезьяны Object - это просто злой бизнес:)

EDIT:

module_function может использоваться так же, как private:

module Something
  def foo
    puts 'foo'
  end

  module_function

  def bar
    puts 'bar'
  end
end

Таким образом, вы можете вызвать Something.bar, но не Something.foo. Если вы определяете другие методы после этого вызова на module_function, они также будут доступны без перемешивания.

Мне это не нравится по двум причинам. Во-первых, модули, которые смешиваются и имеют "статические" методы, звучат немного изворотливыми. Могут быть действительные случаи, но это будет не так часто. Как я уже сказал, я предпочитаю использовать модуль как пространство имен или смешивать его, но не оба.

Во-вторых, в этом примере bar также будет доступен для классов/модулей, которые смешиваются в Something. Я не уверен, когда это желательно, поскольку либо метод использует self, и его нужно смешивать, либо нет, и тогда его не нужно смешивать.

Я думаю, что использование module_function без передачи имени метода используется довольно часто, чем с. То же самое касается private и protected.

Ответ 2

Это хороший способ для библиотеки Ruby предлагать функциональность, которая не использует (много) внутреннее состояние. Поэтому, если вы (например) хотите предложить функцию sin и не хотите загрязнять пространство имен "global" (Object), вы можете определить его как метод класса под константой (Math).

Однако разработчику приложения, который хочет написать математическое приложение, может понадобиться sin каждые две строки. Если метод также является методом экземпляра, она может просто включить модуль Math (или My::Awesome::Nested::Library) и теперь может непосредственно вызвать sin (пример stdlib).

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

Кстати, вы можете добиться аналогичной функциональности, такой как module_function, используя: extend self (в первой строке модуля). На мой взгляд, он выглядит лучше и делает вещи понятнее.

Обновление: Дополнительная информация в в этой статье в блоге.

Ответ 3

Если вы хотите посмотреть на рабочий пример, проверьте хронический камень:

https://github.com/mojombo/chronic/blob/master/lib/chronic/handlers.rb

и Handlers включены в класс Parser:

https://github.com/mojombo/chronic/blob/master/lib/chronic/parser.rb

Он использует module_function для отправки методов из Handlers в конкретные экземпляры Handler, используя этот метод вызова экземпляра.