ネストされたクラスとモジュールにネストされたクラスを使用するのはいつですか?


144

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

class Foo
  class Bar
    # do some useful things
  end
end

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

module Baz
  class Quux
    # more code
  end
end

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

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

回答:


140

他のOOP言語には、上位クラスにバインドしないとインスタンス化できない内部クラスがあります。たとえば、Javaでは

class Car {
    class Wheel { }
}

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

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

Rubyでは

class Car
  class Wheel
  end
end

とは異なり

class Car
end

class Wheel
end

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

しかし、Rubyインタープリターにとっては、名前の違いだけです。

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

module ActiveRecord
  class Base
  end
end

とは異なり

module ActionMailer
  class Base
  end
end

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


5
@rubyprince、私はあなたが関係を確立することによって、何を意味するかわからないCar.newCar::Wheel.new。RubyでCarオブジェクトを初期化するためにオブジェクトを初期化する必要はありませんCar::Wheelが、CarクラスをCar::Wheel使用できるようにロードして実行する必要があります。
Pan Thomakos

30
@ Pan、Java 内部クラスと名前空間を持つRubyクラスを混同しています。非静的Javaネストクラスは内部クラスと呼ばれ、外部クラスのインスタンス内にのみ存在します。外部参照を可能にする隠しフィールドがあります。Rubyの内部クラスは単に名前空間が付けられており、どのような方法でも外側のクラスに「バインド」されていません。これは、Java 静的 (ネストされた)クラスと同等です。はい、答えは多くの票を持っていますが、完全に正確ではありません。
DigitalRoss 2014年

7
この回答が60票を獲得したかどうかはわかりません。ここには文字通り単一の真のステートメントはありません。Rubyには、BetaやNewspeakのようなネストされたクラスはありません。Carとの間にはまったく関係がありませんCar::Wheel。モジュール(つまりクラス)は定数の名前空間にすぎず、Rubyにはネストされたクラスやネストされたモジュールなどはありません。
イェルクWミッターク

4
2つの唯一の違いは、一定の解像度です(これは語彙的であり、2つのスニペットが語彙的に異なるため、明らかに異なります)。ただし、関係するクラスに関しては、2つの間にまったく違いはありません。単純に2つの完全に無関係なクラスがあります。限目。Rubyにはネストされたクラスや内部クラスはありません。ネストされたクラスの定義は正しいですが、簡単にテストできるため、Rubyには適用されませんCar::Wheel.new。ブーム。Wheelオブジェクト内にネストされていないオブジェクトを作成しましたCar
イェルクWミッターク

10
この投稿は、コメントスレッド全体を読まないと誤解を招く可能性があります。
ネイサン

50

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

15
これを内部クラスと呼ばないでください。そうではありません。クラスBはclass 内にありませんA定数は、 Bクラス内名前空間されA、しかしによって参照されるオブジェクトの間には何の関係全く存在しないB(この場合、単にクラスであることを起こるもの)によって参照されるクラスをA
イェルクWミッターク

2
わかりました、「内部」の用語は削除されました。いい視点ね。上記の議論に従わない人のために、論争の理由は、たとえばJavaでこのようなことをすると、内部クラスのオブジェクト(そしてここでは標準的に用語を使用しています)への参照が含まれているためです外部クラスと外部インスタンス変数は、内部クラスメソッドによって参照できます。コードでリンクしない限り、Rubyではそれは起こりません。そして、そのコードが、ahem、enclosureクラスに存在する場合、Barを内部クラスと
DigitalRoss

1
私にとって、モジュールとクラスの間で決定するときに最も役立つのはこのステートメントです:You can't call new on a module.-基本的に、一部のクラスに名前空間を付けたいだけで、実際に外側の「クラス」のインスタンスを作成する必要がない場合は、外部モジュールを使用します。しかし、ラッピング/アウター「クラス」のインスタンスをインスタンス化したい場合は、モジュールではなくクラスにします。少なくともこれは私には理にかなっています。
FireDragon 2018

@FireDragonまたは、サブクラスが継承するファクトリであるクラスが必要な場合や、ファクトリがサブクラスのインスタンスの作成を担当する場合もあります。そのような状況では、モジュールから継承できないため、ファクトリをモジュールにすることはできません。つまり、インスタンス化しない親クラスであり、必要に応じて子の「名前空間」を作成できます(モジュールのようなものです)
rmcsharry

15

これを使用して、クラスをモジュールにグループ化したいと思うでしょう。名前空間のようなもの。

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

Twitter::Client.new

Twitter::Search.new

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

ソースを確認したい場合は、両方のクラスのコードがここここにあります

お役に立てれば!


3
Twitterの宝石更新されたリンク:github.com/sferik/twitter/tree/master/lib/twitter
kode

6

2.5より前のRubyのネストされたクラスとネストされたモジュールの間にはさらに別の違いがあり、他の回答ではここで言及しなければならないと感じていることをカバーできませんでした。これはルックアッププロセスです。

つまり、2.5より前のRubyではトップレベルの定数ルックアップが原因で、ネストされたクラスObjectを使用すると、Rubyはネストされたクラスを誤って(特に)探してしまう可能性があります。

2.5より前のRubyの場合:
ネストされたクラス構造:XネストされたクラスY、またはのクラス があるとしますX::Y。そして、あなたはまたという名前のトップレベルのクラスを持っていますY。場合はX::Yロードされない、次はあなたが呼び出したときX::Y

見つからなかったY中でX、Rubyはの祖先でそれを検索しようとしますX。以来Xモジュールではなくクラスであり、その中には祖先があります[Object, Kernel, BasicObject]。だから、それは探ししようとするY中でObject、それは成功し、それを見つけた場合は、。

しかし、それはトップレベルでYあり、そうではありませんX::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:
最上位の定数検索が削除されました。
このバグに遭遇することを恐れずに、ネストされたクラスを使用できます。


3

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

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

11
「Rubyのクラスはモジュールである」と言う方が正確です。
Tim Diggins、2014年

2
Rubyのすべてがオブジェクトである可能性がありますが、モジュールのクラスの呼び出しは正しくないようです:irb(main):005:0> Class.ancestors.reverse => [BasicObject、Kernel、Object、Module、Class]
Chad M

ここで「is」の定義に
到達します

モジュールはインスタンス化できないことに注意してください。つまり、モジュールからオブジェクトを作成することはできません。したがって、クラスとは異なり、モジュールにはメソッドがありませんnew。あなたはモジュールのクラス(小文字)であると言うことができますが、だから、彼らは、クラス(大文字)と同じものではありません:)
rmcsharry
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.