読者です 読者をやめる 読者になる 読者になる

LGTM

Looks Good To Me

private なsetter メソッドの使いかた

ruby の話です.

private なsetter を なぜかレシーバー付きで呼べるのですが,「そんなん使うの?」という声をときどき聞くのでメモっときます.

おさらい

private なsetter メソッドは,定義に反してself.var= :foo のように呼べる

class Foo
  def test
    self.var = 1
    self.var
  end

  private

  def var
    puts 'getter called'
    @var
  end

  def var=(var)
    puts 'setter called'
    @var = var
  end
end

Foo.new.test

setter(var=()) は呼べますが,getter(var()) は呼べません.

setter called
test.rb:4:in `test': private method `var' called for #<Foo:0x007fea2208c550 @var=1> (NoMethodError)
        from test.rb:21:in `<main>'

参考

private に設定されたメソッドは関数形式でしか呼び出せません。

Ruby 2.1.0 リファレンスマニュアル - 呼び出し制限

  • parse.y (primary): "self[n]=x" can be legal even when "[]=" is private. changes submitted in [ruby-talk:63982]

  • parse.y (aryset): ditto.

  • parse.y (attrset): "self.foo=x" can be legal even when "foo=" is private.

ruby/ruby - GitHub

private なsetter とか使うの?

本題ですが まれに使います.

インスタンス変数になにか代入したいが,直接アクセスしたくない場合とか.

  • Mixin するModule 内で
  • setter に少しロジック入れる場合

どちらも,setter をpublic にしたくないケースではprivate なsetter を好んで使っています.

例1: Mixin するModule 内で

  • class FooBar が共通の機能を持っている
  • その機能で使うインスタンス変数への代入は,おなじclass 内に制限したい
module Feature
  def chop
    @var
  end

  private

  def var=(var)
    @var = var
  end
end

class Foo
  include Feature

  def initialize
    self.var = :foo  # BAD: @var = :foo
  end
end

class Bar
  include Feature

  def initialize
    self.var = :bar  # BAD: @var = :bar
  end
end

Module として切り出している機能に対し,インスタンス変数を直接触るのは 少し密結合になる気がするので,private なsetter でインターフェイスします.

例2: setter に少しロジック入れる

  • インスタンス変数に代入するとき,なにか前処理をしたい (.to_s とか)
  • その代入は,おなじclass 内だけに制限したい
class Foo
  def kick(target)
    self.var = target  # BAD: @var = target.to_s
    # do something
  end

  def slap(target)
    self.var = target  # BAD: @var = target.to_s
    # do another thing
  end

  private

  def var=(var)
    @var = var.to_s
  end
end

前処理は代入直前にしか使わない + DRY にしておきたいので,たぶんこんな形になります.

その他

  • self[]= も一緒
  • @shugomaeda さんのこんなエントリ を見つけた

    その後、IRCでsetter以外のprivateメソッドでもself.fooという書き方を許した方が よいのでは、という話題に。 その後、IRCでsetter以外のprivateメソッドでもself.fooという書き方を許した方が よいのでは、という話題に。