思い出したい人のためのRuby入門 Module編
はじめに
この記事は、あとから振り返ったときに、なるべく短い時間で内容を復習できるようにするために書かれています。
丁寧な説明記事は、長い時間の中でたくさんの方が作ってくださっているので、この記事では丁寧さよりも、簡潔さと網羅性を優先します。
Class編はこちら
誰のための記事か
- 過去にRubyを学習したが、忘れてしまって思い出したい人
誰のためではないか
- Rubyを初めて学ぶ人
Module 概要
Moduleでやりたいこと
- 多重継承せずに機能を共有したい
- 名前の重複を避けたい
Moduleの用途
- 複数のクラスで機能を共有(Mixin)
- 名前空間
- モジュール単体で使う
- アプリで唯一の値にしたい(シングルトン)
ModuleとClass、どっちをつかう?
Moduleを使うとき
- 複数のクラスで共通の機能(振る舞い)を持たせたい
- Moduleでインスタンス変数を「持っているような」書き方もできますが、振る舞いだけを持たせるように設計するべきです。
- is-a関係(継承)が成り立たない
- 名前空間を作りたい
- Railsではディレクトリの階層構造に合わせて名前空間が定義されています。
Classを使うとき
- 同じ振る舞いを持ちながら、異なるデータを持つオブジェクト(インスタンス)を作りたい
- is-a関係(継承)が成り立つ
- データと振る舞いをカプセル化したい
- データとそのデータを操作するメソッドをまとめて、外部から直接アクセスできないようにすること
Moduleの定義
module Greeting
def hello
"Hello!"
end
end
Mixin
- 複数のクラスで機能を共有する
- c.f. クラスは一つのスーパークラスしか持てない
include
- インスタンスメソッドとして追加される
class User
include Greeting
end
user = User.new
puts user.hello # Hello!
ダッグタイピング
ダックタイピングは「アヒルのように歩き、アヒルのように鳴くならば、それはアヒルである」という考え方で、オブジェクトの型よりも、オブジェクトが持つメソッドによって振る舞いを決定するという概念です。
ざっくりいうと、静的型付けのように型(関数やクラスを使う人が持っているべき機能の定義)がカッチリ決まっていなくても、動かすときに動けばええでしょというイメージです。
Rubyではダックタイピングが広く使われています。
moduleは、includeするclassが特定のメソッドを持っていることを前提に書くことができます。
module Greeter
def greeting
# module 内で name を定義していなくても
# include した class がメソッドを持っていることを前提に呼び出す
"My name is #{name}"
end
end
class User
include Greeter
def initialize(name)
@name = name
end
def name
@name
end
end
alice = User.new("Alice")
puts alice.greeting # My name is Alice
extend
- クラスメソッドとして追加される
class User
extend Greeting
end
puts User.hello # Hello!
名前空間
module Admin
class User
def admin?
true
end
end
end
module Guest
class User
def admin?
false
end
end
end
admin_user = Admin::User.new
puts admin_user.admin? # true
guest_user = Guest::User.new
puts guest_user.admin? # false
Module単体で使えるようにする
メソッド module_function
module Greeting
module_function
def hello
"Hello!"
end
end
puts Greeting.hello # Hello!
モジュール自身のメソッドとしてだけでなく
includeしたクラスのインスタンスメソッドとしても利用することができます。
class User
include Greeting
def greeting
hello # privateメソッドとして呼び出せる
end
end
user = User.new
puts user.greeting # Hello!
puts user.hello # private method 'hello' called for an instance of User
特異メソッド(Singleton Method)
特異メソッドとは、特定のオブジェクトに対してのみ定義するメソッドです。
Moduleの特異メソッドは単なる関数の集まりを作りたいという用途で利用します。
module Logger
def self.debug(message)
puts "[DEBUG] #{message}"
end
def self.error(message)
puts "[ERROR] #{message}"
end
end
Logger.debug("message") # [DEBUG] message
Logger.error("message") # [ERROR] message
class << self としても定義できます。
module Logger
def self.debug(message)
puts "[DEBUG] #{message}"
end
def self.error(message)
puts "[ERROR] #{message}"
end
end
特異メソッドはmodule_functionと異なり、includeしても使えません。
そのため、includeもしたいときはmodule_function、単純にmoduleのみに属するメソッドを定義したいときは特異メソッドという使い分けになりそうです。
定数
module Config
API_VERSION = "v1"
BASE_URL = "https://api.example.com"
end
puts Config::API_VERSION # v1
puts Config::BASE_URL # https://api.example.com
:: 名前空間演算子
名前空間の区切りとして使用します。
module Admin
class User
ROLE = "admin"
end
end
puts Admin::User::ROLE # admin
user = Admin::User.new
トップレベルを指定する
::クラス名でトップレベルにあるクラスを参照できます。
モジュール内のクラスがトップレベルの同名のクラスを参照するときに使います。
class User
def self.hello
"Hello from User"
end
end
module Admin
class User
def self.hello
"Hello from Admin::User"
end
def self.call_top_level
::User.hello # トップレベルのUserを指定
end
end
end
``
```rb
puts Admin::User.hello # Hello from Admin::User
puts Admin::User.call_top_level # Hello from User
.と:: 、どっちを使う?
.でも::でも呼び出せますが、階層構造や名前空間を表現するときは::を、メソッド呼び出しをするときには.を使うようです。
-
メソッド呼び出し:
.を使うclass User def self.hello "Hello!" end end -
定数アクセス:
::を使うmodule Config API_VERSION = "v1" end puts Config::API_VERSION # v1 -
クラスやモジュールの指定:
::を使うmodule App module Models class User end end end user = App::Models::User.new
Discussion