24
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

オブジェクト生成時にインスタンス変数の初期値を設定する

Last updated at Posted at 2016-05-30

Railsのmodelではmodel生成時に

user = User.new(name: "tarou", address: "東京都港区", phone: "08099999999"...)

このような形で初期値を設定することが出来ます。

でこれに慣れてるとRubyでこのようなクラスがあったとして、同じようにこんなふうにインスタンス変数設定したいなーと思って書いてもRubyのオブジェクトにそんな機能は備わってないので、Object#initializeを呼んでエラーになってしまいます。

class Bar
  attr_accessor :a, :b, :c
end
Bar.new(a: "A", b: "B", c: "C")
ArgumentError: wrong number of arguments (1 for 0)
from (pry):5:in `initialize'

よくあるやり方としてはinitializeで普通に引数を設定する方法があります。

class Foo
  attr_accessor :a, :b, :c
  def initialize(a, b, c)
    @a = a
    @b = b
    @c = c
  end
end
=> :initialize
Foo.new("A", "B", "C")
=> #<Foo:0x007fec0b61e690 @a="A", @b="B", @c="C">

ただしこのやりかただと変数の数がたくさん増えていった場合にいちいちinitializeの定義を確認してどの順番で引数を渡さないといけなかったかなーと確認しないといけなくなりとても不便です。

そこでもう少し便利な方法を考えます。

キーワード引数を使う

キーワード引数を使うことで引数を関数に与えられた順番ではなく、キーワードに紐付けて渡せるようになります。その結果上記のコードは以下のように書き直せます。これによってどの値にどの値をセットしているか
がよくわかるようになるし、順番も気にする必要がなくなります。

class Bar
  attr_accessor :a, :b, :c
  def initialize(a:, b:, c:)
    @a = a
    @b = b
    @c = c
  end
end
Bar.new(a: "A", b: "B", c: "C") #keyword:値 なのでどの値にどの値をセットするかがわかりやすい
=> #<Bar:0x007fec06199c50 @a="A", @b="B", @c="C">
Bar.new(b: "B", a: "A", c: "C") #順番を入れ替えても問題ない
=> #<Bar:0x007fec0925c388 @a="A", @b="B", @c="C">

ただしこれでもたくさん変数があると

def initialize(a:, b:, c:, d:...)
  @a = a
  @b = b
  @c = c
  @d = d
...
end

となってやや冗長な感じがします。

sendを使う

そこで今度はsendを使って初期値の代入を行います。与えられた引数をまずhash形式に直したうえでsendを使って初期値をセットします。こうすることでいくら初期値がたくさんあってもこれ以上コードが長くなることはなくなりました。ただし、ぱっと見何をやってるかわかりづらいのでここまでやるかは微妙なところかと思います。

class Foo
  attr_accessor :a, :b, :c, :d, :e, :f
  def initialize(a:, b:, c:, d:, e:, f:)
    # 与えられた引数をhash形式で取得
    args_hash = self.method(__callee__).parameters.map do |arg_type,arg|
      [ arg, eval(arg.to_s) ]
    end.to_h
    # sendを使って初期値をセット
    args_hash.each do |prop_name, prop_value|
      self.send("#{prop_name}=",prop_value)
    end
  end
end
Foo.new(a: "A", b: "B", c: "C", d: "D", e: "E", f: "F")
=> #<Foo:0x007f8154748128 @a="A", @b="B", @c="C", @d="D", @e="E", @f="F">
Foo.new(a: "A", b: "B", c: "C")
ArgumentError: missing keywords: d, e, f #何が足りていないかもわかりやすい。ただし何も入れたくない場合は明示的にnilをセットする必要がある。

まだまだ初学者+エンジニア1人でやっているのでおかしいところがあれば是非ご指摘いただけると嬉しいです。
参考: http://qiita.com/hidechae/items/5416a996529d7663c182

24
14
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?