web-dev-qa-db-ja.com

ネストされたクラスとモジュールにネストされたクラスを使用する場合

私はサブクラスとモジュールをいつ使用するかについてよく知っていますが、最近では次のようなネストされたクラスを見てきました:

class Foo
  class Bar
    # do some useful things
  end
end

同様にモジュールにネストされたクラス:

module Baz
  class Quux
    # more code
  end
end

ドキュメントと記事のどちらかがまばらであるか、適切な検索用語を模索するのに十分なテーマに関する教育を受けていませんが、トピックに関する多くの情報を見つけることができないようです。

誰かがそれらの技術がなぜ/いつ使用されるかについての例や投稿へのリンクを提供できますか?

132
cmhobbs

他のOOP言語には 内部クラス があり、上位レベルのクラスにバインドされていないとインスタンス化できません。たとえば、Javaでは、

class Car {
    class Wheel { }
}

CarクラスのメソッドのみがWheelsを作成できます。

Rubyにはそのような振る舞いはありません。

Rubyでは、

class Car
  class Wheel
  end
end

とは異なり

class Car
end

class Wheel
end

クラスの名前のみWheel vs. Car::Wheel。この名前の違いにより、Car::Wheelクラスは一般的なホイールではなく、車のホイールのみを表すことができることをプログラマーに明示できます。 Rubyにクラス定義をネストすることは好みの問題ですが、2つのクラス間のコントラクトをより強力に実施するという意味で目的を果たし、そうすることでクラスとその使用に関するより多くの情報を伝えます。

ただし、Rubyインタープリターにとっては、名前の違いにすぎません。

2番目の観察に関しては、通常、モジュールの内部にネストされたクラスがクラスの名前空間に使用されます。例えば:

module ActiveRecord
  class Base
  end
end

とは異なり

module ActionMailer
  class Base
  end
end

モジュール内にネストされたクラスの使用はこれだけではありませんが、一般的に最も一般的です。

128
Pan Thomakos

Rubyでは、ネストされたクラスを定義することは、モジュールでクラスを定義することに似ています。実際にクラス間の関連付けを強制するのではなく、定数の名前空間を作成するだけです。 (クラス名とモジュール名は定数です。)

受け入れられた答えは何についても正しくありませんでした。1 以下の例では、既存の囲い込みクラスのインスタンスなしで、字句的に囲まれたクラスのインスタンスを作成します。

class A; class B; end; end
A::B.new

利点は、モジュールの利点と同じです。カプセル化、1か所でのみ使用されるコードのグループ化、使用される場所の近くにコードを配置します。大規模なプロジェクトには、各ソースファイルで繰り返し発生する外部モジュールが1つあり、多くのクラス定義が含まれています。さまざまなフレームワークとライブラリコードがすべてこれを行う場合、それらはトップレベルにそれぞれ1つの名前のみを提供し、競合の可能性を減らします。確かに平凡ですが、それが使用される理由です。

モジュールの代わりにクラスを使用して外側の名前空間を定義することは、1ファイルプログラムまたはスクリプトで、または既に何かにトップレベルクラスを使用している場合、または実際にクラスをリンクするコードを追加する場合に意味があります真内部クラススタイル。 Rubyには内部クラスはありませんが、コードで同じ動作を作成することを妨げるものは何もありません。内側のオブジェクトから外側のオブジェクトを参照するには、外側のオブジェクトのインスタンスからドットを挿入する必要がありますが、クラスをネストすると、これが実行される可能性があることが示唆されます。慎重にモジュール化されたプログラムは、常に最初に包含クラスを作成し、ネストされたクラスまたは内部クラスで合理的に分解される場合があります。モジュールでnewを呼び出すことはできません。

楽しさと練習のためだけに、名前空間がそれほど必要ではないスクリプトでも、一般的なパターンを使用できます...

#!/usr/bin/env Ruby

class A
  class Realwork_A
    ...
  end
  class Realwork_B
    ...
  end

  def run
    ...
  end

  self
end.new.run
47
DigitalRoss

これを使用して、クラスをモジュールにgroupすることができます。名前空間のものの並べ替え。

たとえば、 Twitter gem は名前空間を使用してこれを実現します。

Twitter::Client.new

Twitter::Search.new

したがって、ClientクラスとSearchクラスの両方がTwitterモジュールの下にあります。

ソースを確認する場合は、両方のクラスのコードが here および here にあります。

お役に立てれば!

15
Pablo Fernandez

2.5より前のRubyのネストされたクラスとネストされたモジュールには、他の答えがカバーできなかったという別の違いがあります。これは検索プロセスです。

要するに:2.5より前のRubyでのトップレベルの定数ルックアップのため、Rubyは、間違った場所(Object特に)ネストされたクラスを使用する場合。

In Ruby 2.5より前:
ネストされたクラス構造:クラスX、ネストされたクラスY、またはX::Yがあるとします。そして、Yという名前の最上位クラスがあります。 X::Yがロードされていない場合、X::Yを呼び出すと次のようになります。

YXが見つからない場合、RubyはXの先祖で検索しようとします。 Xはクラスでありモジュールではないため、祖先があり、その中には[Object, Kernel, BasicObject]があります。そのため、YObjectを探し、正常に検出します。

まだX::Yではなく、Yの最上位です。次の警告が表示されます。

warning: toplevel constant Y referenced by X::Y


ネストされたモジュール構造:前の例でXがクラスでなくモジュールであるとします。

モジュールは自身を祖先としてのみ持っています:X.ancestors[X]を生成します。

この場合、RubyはYの祖先の1つでXを検索できず、NameErrorをスローします。 Rails(またはオートロードを備えた他のフレームワーク)は、その後X::Yをロードしようとします。

詳細については、この記事を参照してください。 https://blog.jetbrains.com/Ruby/2017/03/why-you-should-not-use-a-class-as-a- namespace-in-Rails-applications /

In Ruby 2.5:
トップレベルの定数検索が削除されました。
このバグに遭遇することを恐れずに、ネストされたクラスを使用できます。

4
Tim N

前の回答に加えて:Rubyのモジュールはクラスです

$ irb
> module Some end
=> nil
> Some.class
=> Module
> Module.superclass
=> Object
3
demas