Moduleの名前空間について簡単にまとめます。
書籍中心に勉強していますので、ご指摘あればコメントお願いいたします。
名前空間とは
Moduleを使用して、クラスなどの定義した定数が一意になるようにするための手段です。
開発を進めていくと、他のファイルにあるクラス名を重複するなどの問題が起こる。これを解決する手段として用いられます。
定義の仕方
定義したクラス名にmoduleをネストして使用します。
module Bar
class Foo
def say
"hello"
end
end
end
foo = Bar::Foo.new
foo.say #=> "hello"
呼び出す場合はモジュール名::クラス名
の形を取ります。
ここで、インスタンスを作るときに、Foo.new
だけの形で定義するとエラーが出力されるので注意しましょう。
使用する場面
名前空間を使用する時はプロジェクトのディレクトリ構造に名前空間を合わせます。
上のクラスはBarディレクトリのFooクラスで定義されているということになります。
つまりBar::Foo
といる名前は、Bar/Foo.rb
というファイルに対応していることになります。
定数を定義した時
名前空間を使用して、定数を参照したい場合を考えてみましょう。
module Bar
KEY = "password0123"
class Foo
def say(key = KEY)
"hello, #{key}"
end
end
end
foo = Bar::Foo.new
foo.say # => "hello, password0123"
上の記述の場合は、定数KEY
を参照することができます。
これは、Barモジュール
がFooクラス
のスコープ内にあるため、参照できています。
では、moduleをクラスの外側に定義してみましょう。
module Bar
KEY = "password0123"
end
class Bar::Foo
def say(key = KEY)
"hello, #{key}"
end
end
foo = Bar::Foo.new
foo.say # => NameError
この場合はエラーが発生します。
::
を使用して名前空間を定義していますが、KEY
定数を参照指定いないようです。
これには、RUBYの定数の探索の仕組みが関係しています。
RUBYの定数の探索
定数の探索の方法は2つあります。
- 継承階層の探索
- レキシカルスコープの探索
継承階層の探索は、メソッドなどを探索する場合に子クラスから親クラスへ遡っていく方法です。
この継承階層の探索をする前に、まず定数が定義されているレキシカルスコープ内を探索します。
module Bar
KEY = "password0123"
class Foo
def say(key = KEY)
"hello, #{key}"
end
end
end
foo = Bar::Foo.new
foo.say # => "hello, password0123"
最初の例では、moduleがclassをネストしている形をとり、同じレキシカルスコープ内と言えます。
Fooクラスの中で呼び出しているため、KEY定数
を使用することができます。
では、エラーが出た方の記述です。
module Bar
KEY = "password0123"
end
class Bar::Foo
def say(key = KEY)
"hello, #{key}"
end
end
foo = Bar::Foo.new
foo.say # => NameError
これは、moduleがclassの外でendしていますので、レキシカルスコープから外れています。
名前空間でBarモジュールを::
で修飾していますが、定数の参照はできないようです。
解決法としては、KEY定数にも同様に::
を使用することで定数を使用することができます。
module Bar
KEY = "password0123"
end
class Bar::Foo
def say(key = Bar::KEY)
"hello, #{key}"
end
end
foo = Bar::Foo.new
foo.say # => "hello, password0123"
モジュールでネスとしていないクラスは無名?
クラスを定義するとき、すべてのクラスに名前空間をつけるわけではありません。
では、名前空間を使っていないクラスには名前がないのでしょうか?
実は、名前のつかないクラスはすべてトップクラスのObjectクラスに存在します。
class Bar
def say
"hello"
end
end
bar = Object::Bar.new # =>::Bar.newでもok
p bar.say # => "hello"
この場合、わざわざObjectクラスを参照して定義する必要は全くありません。
このようなトップクラスに存在するクラスをグローバル名前空間に属するとしています。
ちなみにObject::Bar
を省略した形で、::Bar
でも呼び出せます。
使用する場面としては、元々Rubyに定義されているArrayクラスを名前空間で定義して、その別名でつけたArrayクラスの中で元にあるArrayクラスを使用したい場合などに活躍します。
module Cluster
class Array
def initialize (n)
@disks = ::Array.new(n){|i| "disk#{i}"}
end
end
end
cluster = Cluster::Array.new(3)
# => #<Cluster::Array:0x000055fe3b0a6448 @disks=["disk0", "disk1", "disk2"]>
# 書籍を参照
終わりに
名前空間のレキシカルスコープについて、トップクラスでのグローバル名前空間については理解が深まった気がします。
gemのライブラリに書かれている::
に恐れずに立ち向かえそうです...!
参考書籍
Effective Ruby