web-dev-qa-db-ja.com

ruby module_function vsモジュールを含める

Rubyでは、ここに示すようにmodule_functionを使用することで、モジュールを混合せずにモジュール関数を使用できることを理解しています。これがどのように役立つかがわかるので、モジュール内で混合することなく関数を使用できます。

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

私の質問は、なぜこれらの両方の方法で関数を定義したいのかということです。

なぜ持っていないのですか

def MyModule.do_something

OR

def do_something

どのような場合に、関数を混合したり、静的メソッドとして使用したりできるのが便利でしょうか?

47
Jeff Storey

Enumerable と考えてください。

これは、モジュールに含める必要がある場合の完璧な例です。クラスで#eachを定義している場合、モジュール(#map#selectなど)を含めるだけで多くの利点が得られます。これは、モジュールをミックスインとして使用する場合の唯一のケースです。モジュールが、そのモジュールをインクルードするクラスで定義されたいくつかのメソッドに関して機能を提供する場合です。これが一般的な唯一のケースであるべきだと主張できます。

「静的」メソッドの定義に関して、より良いアプローチは次のとおりです。

module MyModule
  def self.do_something
  end
end

#module_functionを呼び出す必要はありません。私はそれがただ奇妙な遺産のものだと思う。

これを行うこともできます:

module MyModule
  extend self

  def do_something
  end
end

...しかし、モジュールをどこかに含めたい場合はうまくいきません。 Ruby metaprogramming。の微妙さを学ぶまで、それを避けることをお勧めします。

最後に、あなたがそうするだけなら:

def do_something
end

...最終的にはグローバル関数としてではなく、Objectのプライベートメソッドとして機能します(Rubyには関数はなく、メソッドのみです)。 2つの欠点があります。まず、名前空間がありません。同じ名前の別の関数を定義すると、後で評価される関数になります。第二に、#method_missingに関して機能を実装している場合、Objectにプライベートメソッドがあると、それがシャドウされます。そして最後に、モンキーパッチObjectは単なる悪いビジネスです:)

編集:

module_functionは、privateと同様の方法で使用できます。

module Something
  def foo
    puts 'foo'
  end

  module_function

  def bar
    puts 'bar'
  end
end

そうすれば、Something.barを呼び出すことができますが、Something.fooを呼び出すことはできません。このmodule_functionの呼び出し後に他のメソッドを定義すると、それらは混ざることなく使用できます。

ただし、2つの理由で好きではありません。第一に、両方が混在し、「静的」メソッドを備えたモジュールは少し危険です。有効なケースがあるかもしれませんが、それほど頻繁ではありません。先ほど言ったように、私はモジュールを名前空間として使用するか、または混在させることを好みますが、両方は使いません。

第二に、この例では、barは、Somethingが混在するクラス/モジュールでも使用できます。メソッドがselfを使用しており、混合する必要があるか、そうでない場合は混合する必要がないため、これがいつ望ましいかはわかりません。

メソッドの名前を渡さずにmodule_functionを使用することは、使用するよりもかなり頻繁に使用されると思います。 privateprotectedについても同様です。

41
Stefan Kanev

これは、Rubyライブラリが(多くの)内部状態を使用しない機能を提供するための良い方法です。したがって、sin関数を提供し、汚染を望まない場合「グローバル」(Object)名前空間、定数(Math)の下でクラスメソッドとして定義できます。

ただし、数学アプリケーションを作成するアプリ開発者は、2行ごとにsinを必要とする場合があります。メソッドがインスタンスメソッドでもある場合、彼女はMath(またはMy::Awesome::Nested::Library)モジュールを含めるだけで、sin(stdlibの例)を直接呼び出すことができます。

それは本当にユーザーにとってライブラリをより快適にすることです。最上位のライブラリの機能が必要な場合は、自分で選択できます。

ところで、module_function(モジュールの最初の行)を使用して、extend selfなどの同様の機能を実現できます。私の考えでは、見た目が良くなり、理解しやすくなりました。

更新:このブログ記事 の詳細な背景情報。

14
J-_-L

実際の例をご覧になりたい場合は、慢性的な宝石をご覧ください。

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

ハンドラーは、ここのParserクラスに含まれています。

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

彼はmodule_functionを使用して、そのインスタンスのinvokeメソッドを使用して、ハンドラーからハンドラーの特定のインスタンスにメソッドを送信します。

2
TalkativeTree