更新日:
【Ruby】 attr_writerメソッド使ってコードの可読性を上げよう
attr_writerとは、インスタンス変数の書き込み専用のメソッド(セッターメソッド)を自動で定義することが出来るメソッドのことです。
Rubyではクラスのインスタンス変数を外部から変更する場合は、以下のように書き込み専用メソッドを定義する必要があります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class User
attr_reader :name, :age # @name,@ageの変更を確認するために記述
def initialize(name, age)
@name = name
@age = age
end
def name=(name) # 書き込み専用メソッド
@name = name
end
def age=(age) # 書き込み専用メソッド
@age = age
end
end
tanaka = User.new('田中太郎', 18)
p tanaka.name #=> "田中太郎"
p tanaka.age # => 18
tanaka.name = '田中花子' # @nameを変更する
tanaka.age = 33 # @ageを変更する
p tanaka.name #=> "田中花子"
p tanaka.age # => 33
上記のコードだと変更したいインスタンス変数が増えるたびに書き込み専用のメソッドを定義する必要があるので、コード量が増えて可読性が下がります。
しかし、以下のコードのようにattr_writerメソッドを使えば、わざわざ明示的に書き込み専用のメソッドを定義する必要がないのでコード量も少なく済みます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class User
attr_reader :name, :age
attr_writer :name, :age # これで書き込み専用のメソッドが定義される
def initialize(name, age)
@name = name
@age = age
end
end
tanaka = User.new('田中太郎', 18)
p tanaka.name #=> "田中太郎"
p tanaka.age # => 18
tanaka.name = '田中花子' # @nameを変更する
tanaka.age = 33 # @ageを変更する
p tanaka.name #=> "田中花子"
p tanaka.age # => 33
このように、attr_writerメソッドに変更したいインスタンス変数名を指定するだけで、そのインスタンス変数の書き込み専用のメソッドを定義することが出来ます。
attr_writerメソッドの使い方
attr_writerメソッドが何をしているのか良く分からないという方は、getter/setter(ゲッター/セッター)を理解してないことが原因の1つとしてあります。
この章では、attr_writerメソッドの使い方だけではなく、大きく関係するsetter(セッター)も一緒に解説するので、attr_writerメソッドの必要性や使用場面を具体的に理解することが出来ます。
基本構文
attr_writerメソッドは、以下のように文字列かシンボルで変更したいインスタンス変数名を指定します。
1
2
3
4
5
# シンボルの場合
attr_writer :インスタンス変数名
# 文字列の場合
attr_writer 'インスタンス変数名'
インスタンス変数名は、以下のように@なしで記述します。
1
2
3
4
5
# シンボルの場合
attr_writer :name
# 文字列の場合
attr_writer 'name'
上記のコードの記述によって自動で定義される書き込みメソッドは、以下の通りです。(詳細は後述します。)
1
2
3
def name=(name)
@name = name
end
また、複数指定する場合は以下のようにカンマで区切ります。
1
2
3
4
5
# シンボルの場合
attr_writer :name, :age
# 文字列の場合
attr_writer 'name', 'age'
セッターメソッドの必要性
クラスで定義したインスタンス変数をクラスの外部からアクセスするには、インスタンスメソッドで経由させる必要があります。
例えば、以下のtanaka.set_age(33)
はset_ageインスタンスメソッドを経由しているので、クラス外部からでも@ageを変更することが出来ています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class User
attr_reader :name, :age # @name, @ageの変更を確認するために記述
def initialize(name, age)
@name = name
@age = age
end
def set_age(age)
@age = age
end
end
tanaka = User.new('田中太郎', 18)
p tanaka.age
#=> 18
tanaka.set_age(33) # インスタンスメソッド経由で@ageを変更する
p tanaka.age
#=> 33
しかし、以下のコードのようにインスタンスメソッドを経由せずに直接tanaka.@age = 33
で変更しようとするとエラーが発生します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class User
attr_reader :name, :age # @name, @ageの変更を確認するために記述
def initialize(name, age)
@name = name
@age = age
end
end
tanaka = User.new('田中太郎', 18)
p tanaka.age
#=> 18
tanaka.@age = 33
#=> syntax error, unexpected tIVAR, expecting '(' tanaka.@age = 33
クラスの外部からインスタンス変数を変更する場合には、インスタンスメソッドを経由して変更する必要があります。
セッターメソッドとは?
インスタンス変数の書き込み専用のインスタンスメソッドを「セッターメソッド」と呼びます。セッターメソッドは、以下のname=メソッド
やage=メソッド
のように、メソッドが呼び出された時に各インスタンス変数の値を変更するように定義します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class User
def initialize(name, age)
@name = name
@age = age
end
def name=(name) # セッターメソッド
@name = name #メソッドが呼び出されたら@nameを引数の値に変更
end
def age=(age) # セッターメソッド
@age = age #メソッドが呼び出されたら@ageを引数の値に変更
end
end
tanaka = User.new('田中太郎', 18)
tanaka.age = 33
上記のセッターメソッドのようにメソッド名の末尾に=(イコール)をつけると、代入式のようにメソッドを呼び出すことが出来ます。
例えば、以下のtanaka.age = 33
はtanaka.age
に33
を代入しているように見えますが、実際には引数に33を渡してage=メソッドを呼び出しているのです。
1
2
3
4
5
6
7
8
9
10
11
class User
def age=(age)
@age = age # ここで@ageの値が33に変更されている
end
end
# 代入しているように見えるが。。。
tanaka.age = 33
# 実際には、以下のようにage=メソッドを呼び出している
tanaka.age=(33)
それでは、実際に@age
が33
に変更されてるか確認できるように、以下のようにattr_readerメソッドを追加します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class User
attr_reader :age # @ageをクラス外部から参照出来るようにする
def initialize(name, age)
@name = name
@age = age
end
def name=(name) # セッターメソッド
@name = name #メソッドが呼び出されたら@nameを引数の値に変更
end
def age=(age) # セッターメソッド
@age = age #メソッドが呼び出されたら@ageを引数の値に変更
end
end
tanaka = User.new('田中太郎', 18)
p tanaka.age
# => 18
tanaka.age = 33 # age=メソッドを呼び出して@ageを変更する
p tanaka.age
# => 33 # @ageが33に変更されている!
上記のコードのtanaka.age = 33
によってage=というセッターメソッド(書き込み専用のインスタンスメソッド)を経由することでインスタンス変数(@age)をクラスの外部から変更することが出来ています。
attr_writerメソッドの役割とは?
セッターメソッドでクラス外部からインスタンス変数を変更することが出来ましたが、変更したいインスタンス変数が増える度にセッターメソッドを定義しなければなりません。
例えば、@nameや@age以外にも@mailや@cellを変更したい場合は、以下のコードのようにセッターメソッドを定義するので、コードがどんどん増えて可読性が下がります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class User
def initialize(name, age, mail, cell)
@name = name
@age = age
@mail = mail
@cell = cell
end
def name=(name) # セッターメソッド
@name = name
end
def age=(age) # セッターメソッド
@age = age
end
def mail=(mail) # 新たに追加したセッターメソッド
@mail = mail
end
def cell=(cell) # 新たに追加したセッターメソッド
@cell = cell
end
end
しかし、attr_writerメソッド
を使えば、明示的に1つ1つのセッターメソッドを定義する必要もなく以下の記述だけで済みます。
1
2
3
4
5
6
7
8
9
10
11
12
13
class User
attr_reader :name, :age, :mail, :cell # インスタンス変数参照の為
# 以下の記述だけで各セッターメソッドが定義される
attr_writer :name, :age, :mail, :cell
def initialize(name, age, mail, cell)
@name = name
@age = age
@mail = mail
@cell = cell
end
end
attr_writerメソッドを使った場合でも、以下のように各インスタンス変数の変更が出来ています。
1
2
3
4
5
6
7
tanaka = User.new('田中太郎', 18, 'hoge@example.com', '070-09xx-xxxx')
p tanaka.age
# => 18
tanaka.age = 33 # age=メソッドを呼び出して@ageを変更する
p tanaka.age
# => 33
インスタンス変数の変更ができている理由は、以下のコードのようにattr_writer :age
と記述すると@ageを変更するためのセッターメソッドが定義されるからです。
1
2
3
4
5
6
attr_writer :age
# 以下のメソッドは、上記の記述で自動で定義されるメソッド
def age=(age)
@age = age
end
このように、attr_writerメソッドはセッターメソッドを定義することが出来ます。
他のアクセサメソッドとの違い
インスタンス変数の書き込み(変更)専用メソッドの「セッターメソッド」の他にも、インスタンス変数の読み取り(参照)専用メソッドである「ゲッターメソッド」があります。
以下のコードのようにゲッターメソッドを定義すると、クラス外部からインスタンス変数の値を参照することが出来ます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class User
def initialize(name, age)
@name = name
@age = age
end
def age # ゲッターメソッド
@age # メソッドが呼び出されたら@ageを返す
end
end
tanaka = User.new('田中太郎', 18)
p tanaka.age # ageメソッドを呼び出す
#=> 18
上記では、ゲッターメソッドを定義した事でインスタンス変数の参照を行うことが出来ました。
しかし、以下のコードのようにセッターメソッドを定義するattr_writerメソッドの記述のみでは、インスタンス変数を変更するだけでゲッターメソッドのような値を参照する機能はないので注意してください。
1
2
3
4
5
6
7
8
9
10
11
12
13
class User
attr_writer :name, :age
def initialize(name, age)
@name = name
@age = age
end
end
tanaka = User.new('田中太郎', 18)
p tanaka.age
# => undefined method `age' for #<User:0x00007f98300938a8 @name="田中太郎", @age=18> (NoMethodError)
このようにインスタンス変数の値を参照する場合はゲッターメソッドか、ゲッターメソッドを定義するattr_readerメソッドが必要になります。詳細は「attr_readerメソッドの使い方」を参考にしてください。
attr系メソッドまとめ
アクセサメソッドを定義してくれるメソッドは、attr_writerメソッドの他にもattr_readerメソッド、attr_accessorメソッドがあります。各メソッドの特徴は、以下の表の通りです。
attr系メソッド | 定義されるメソッド | インスタンス変数の値に対して |
---|---|---|
attr_writer | セッターメソッドを定義 | 変更のみ |
attr_reader | ゲッターメソッドを定義 | 参照のみ |
attr_accessor | ゲッター/セッター両方を定義 | 参照と変更の両方 |
ゲッターメソッドとセッターメソッドの両方を定義し、インスタンス変数の値を参照と変更の両方可能にするのがattr_accessorメソッドです。こちらのメソッドもよく使われるので、まだ知らない人は「attr_accessorメソッドの使い方」を参考にしてください。
この記事のまとめ
- attr_writerメソッドは、セッターメソッドを定義してくれるメソッド
- セッターメソッドとは、インスタンス変数の値を変更する為のメソッド
- attr_writerメソッドは、インスタンス変数の値の変更のみで参照することは出来ない