Crystalのモジュールには、2つの目的があります。
1つ目の目的は、名前の衝突を避けるために名前空間を作ることです。 また、コードをまとめることで、そのコードが何のためのものかを理解しやすくもします。
2つ目の目的は、型の間でコードを共有するためのミックスインを定義することです。
モジュールはクラスに似ていますが、主な違いは、インスタンス化できないためインスタンス変数を持たないことです。
モジュールを宣言するには、moduleキーワードの後にモジュール名を書きます。
module Foo
end
名前空間は、コードをまとめるための仕組みです。
これにより名前の衝突を避けられ、そのコードが何のためのものかを理解しやすくなります。
名前空間の中に置かれた定数やクラスにアクセスしたいときは、::演算子を使います。
module Foo
class Bar
end
end
Foo::Bar.new
これは、たとえば複数のクラスが同じ「ベース」の機能を必要とするときや、無関係なクラス間でコードを共有したいときに役立ちます。 関係のないクラス同士でコードを共有したいときにも便利です。
モジュールをミックスインとして使う方法は2つあります。1つ目はincludeキーワードを使う方法で、2つ目はextendキーワードを使う方法です。
どちらの方法でも、モジュールをincludeまたはextendした型で定数が使えるようになります。
includeは、モジュール内のすべてのメソッドを、そのモジュールをincludeした型のインスタンスメソッドとして使えるようにします。
includeキーワードは型の先頭に書き、その後にモジュール名を続けます。
module Foo
def foo
"foo"
end
end
class Bar
include Foo
end
Bar.new.foo # => "foo"
extendはincludeと似たように動作しますが、メソッドをインスタンスメソッドとして使えるようにするのではなく、クラスメソッドとして使えるようにします。
extendキーワードは型の先頭に書き、その後にモジュール名を続けます。
module Foo
def foo
"foo"
end
end
class Bar
extend Foo
end
Bar.foo # => "foo"
Crystalでは、モジュールでextend selfパターンを使うのが典型的なパターンです。
これにより、モジュール内のすべてのメソッドが、モジュール自身のクラスメソッドとして使えるようになります。
つまり、def self.method_name構文を使って各メソッドをモジュールに割り当てる必要がありません。
module Foo
extend self
def foo
"foo"
end
end
Foo.foo # => "foo"