私はサブクラスとモジュールをいつ使用するかについてよく知っていますが、最近では次のようなネストされたクラスを見てきました:
class Foo
class Bar
# do some useful things
end
end
同様にモジュールにネストされたクラス:
module Baz
class Quux
# more code
end
end
ドキュメントと記事のどちらかがまばらであるか、適切な検索用語を模索するのに十分なテーマに関する教育を受けていませんが、トピックに関する多くの情報を見つけることができないようです。
誰かがそれらの技術がなぜ/いつ使用されるかについての例や投稿へのリンクを提供できますか?
他のOOP言語には 内部クラス があり、上位レベルのクラスにバインドされていないとインスタンス化できません。たとえば、Javaでは、
class Car {
class Wheel { }
}
Car
クラスのメソッドのみがWheel
sを作成できます。
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
モジュール内にネストされたクラスの使用はこれだけではありませんが、一般的に最も一般的です。
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
これを使用して、クラスをモジュールにgroupすることができます。名前空間のものの並べ替え。
たとえば、 Twitter gem は名前空間を使用してこれを実現します。
Twitter::Client.new
Twitter::Search.new
したがって、Client
クラスとSearch
クラスの両方がTwitter
モジュールの下にあります。
ソースを確認する場合は、両方のクラスのコードが here および here にあります。
お役に立てれば!
2.5より前のRubyのネストされたクラスとネストされたモジュールには、他の答えがカバーできなかったという別の違いがあります。これは検索プロセスです。
要するに:2.5より前のRubyでのトップレベルの定数ルックアップのため、Rubyは、間違った場所(Object
特に)ネストされたクラスを使用する場合。
In Ruby 2.5より前:
ネストされたクラス構造:クラスX
、ネストされたクラスY
、またはX::Y
があるとします。そして、Y
という名前の最上位クラスがあります。 X::Y
がロードされていない場合、X::Y
を呼び出すと次のようになります。Y
にX
が見つからない場合、RubyはX
の先祖で検索しようとします。 X
はクラスでありモジュールではないため、祖先があり、その中には[Object, Kernel, BasicObject]
があります。そのため、Y
でObject
を探し、正常に検出します。
まだ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:
トップレベルの定数検索が削除されました。
このバグに遭遇することを恐れずに、ネストされたクラスを使用できます。
前の回答に加えて:Rubyのモジュールはクラスです
$ irb
> module Some end
=> nil
> Some.class
=> Module
> Module.superclass
=> Object