Crystal의 모듈은 두 가지 목적으로 사용해요.
첫 번째 목적은 이름 충돌을 피하기 위해 네임스페이스를 만드는 거예요. 또한 코드를 묶어 주기 때문에, 그 코드가 무엇을 위한 것인지 더 쉽게 이해할 수 있게 해줘요.
두 번째 목적은 타입끼리 코드를 공유하기 위한 믹스인을 정의하는 거예요.
모듈은 클래스와 비슷하지만, 가장 큰 차이는 인스턴스를 만들 수 없다는 점이에요. 그래서 인스턴스 변수도 없어요.
모듈을 선언하려면 module 키워드 뒤에 모듈 이름을 써요.
module Foo
end
네임스페이스는 코드를 묶는 방법이에요.
이름 충돌을 피하고, 그 코드가 무엇을 위한 것인지 더 쉽게 이해할 수 있게 해줘요.
네임스페이스 안에 넣어 둔 상수나 클래스에 접근하고 싶을 때는 :: 연산자를 사용해요.
module Foo
class Bar
end
end
Foo::Bar.new
예를 들어 여러 클래스가 같은 "기본" 기능을 필요로 하거나, 서로 관련 없는 클래스끼리 코드를 공유해야 할 때 유용해요. 관련 없는 클래스 사이에서 코드를 공유하고 싶을 때도 마찬가지예요.
모듈을 믹스인으로 사용하는 방법에는 두 가지가 있어요. 첫 번째는 include 키워드를 사용하는 것이고, 두 번째는 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"