Ruby: インスタンス変数の初期化とカスケードパターン

attr_accessor でインスタンス変数のアクセサは簡単に作れるけど、initialize の引数で値の初期化をしていると変更があった時に面倒。。。と思って書いたメモです。
ふーん、こいつはこんなことしたんだ、くらいに適当にお読みください。

# カスケードパターン
```rb
  recevier.message(arg1, arg2, ... argN)
```
上は Ruby のメソッド呼び出しの形です。これは send メソッドを使って以下のように書けます。

```rb
  receiver.send :message, arg1, arg2, ... argN
```

「モノ(オブジェクト)とモノがメッセージのやり取りをする」(メッセージパッシング)をよく表しているスタイルだと思います。
メッセージパッシングは、オブジェクト指向の重要な概念だと思います。

カスケードパターンは、レシーバ(メッセージを受け取るオブジェクト)に一度に複数のメッセージを投げるデザインパターンです。

他言語ですが、CoffeeScript (jQueryを使用) の例です。(雰囲気だけ見ていただればいいです)

```coffeescript
  $ '#stage'                                  # オブジェクト「$ 'stage'」に対して(レシーバに対して)
    .text 'Drop file here.'                   # text メソッドを呼び出し(メッセージを送る)
    .on 'dragover', dnd()                     # on メソッドを呼び出し(メッセージを送る)
    .on 'drop',     dnd (e) -> console.log e  # on メソッドを呼び出し(メッセージを送る)
```

カスケードパターン(Cascade) は以下の書籍で紹介されています。

- 『ケント・ベックの Smalltalk ベストプラクティス・パターン』
  - ピアソンエデュケーション (2003/03)
  - ISBN-10: 4894717549
  - ISBN-13: 978-4894717541  


# アクセサと attr_accessor
アクセサはインスタンス変数にアクセスするためのメソッドです。
アクセサには getter と setter があります。
getter は、レシーバに変数の値を取得する、というメッセージを投げます。(値を取得するメソッド)
setter は、レシーバに変数の値を設定する、というメッセージを投げます。(値を設定するメソッド)

Ruby にはアクセサを簡単につくる attr_accessor メソッドがあります。
attr_accessor はクラス(定義)文の中で以下のように使えます。

```rb
class C
  attr_accessor :foo
end
```

上のコードは下のコードと同じことです。(アクセサが定義されます)

```rb
class C
  def foo                 # getter
    @foo
  end

  def foo=(val)           # setter
    @foo = val
  end
end
```

attr_accessor の引数がシンボル「`:foo`」の場合、アクセサとインスタンス変数の名前は、以下のようになります。

- getter は「`foo`」になる。(シンボルの頭の「`:`」を取った名前)
- setter は「`foo=`」になる。(シンボルの頭の「`:`」を取って、末尾に「`=`」を付けた名前)
- インスタンス変数は「`@foo`」になる。(シンボルの頭の「`:`」を「`@`」に替えた名前)

アクセサは以下のように呼び出されます。

```rb
obj = C.new

obj.foo = 'hello'    # setter 呼び出し: obj.foo=('hello') とも書き換えられる
p obj.foo            # getter 呼び出し: obj.foo()         とも書き換えられる    
```

参考。
- [instance method Module#attr_accessor (Ruby 2.1.0 リファレンスマニュアル)](http://docs.ruby-lang.org/ja/2.1.0/method/Module/i/attr_accessor.html)

# インスタンス変数の初期化
インスタンス変数は未定義であってもエラーにならずアクセスできます。(定義はされません)
その時に返る値は nil です。

インスタンス変数の初期値をクラスを使うユーザに設定させたい場合があります。
例えば、インスタンスを new する時に以下のように設定させたいとします。

```rb
c = C.new('hello')
``` 

上の要求は、initialize メソッドを以下のように実装することで実現できます。

```rb
class C
  attr_accessor :foo

  def initialize(foo)
    @foo = foo
  end
end
```

余談ですが、上のコードは下のように書き換えられます。(この書き方がいいかどうかは別問題です)

```rb
class C
  attr_accessor :foo

  alias initialize foo=    # この場合の initialize の形は foo= と同じなので alias しちゃう
end
```

# カスケードパターン initializer
ここからが、本題です。
アクセサをもったインスタンス変数(以降、プロパティと呼びます)が、だんだん多くなってくると、その度に initialize のインタフェース設計を変更する必要があり面倒です。
(面倒でなくても、ここでは面倒と思ってください)

```rb
class C
  attr_accessor :foo1, :foo2, :foo3, ... (略)

  def initialize(foo1, foo2, foo3, ...)  # プロパティが増える度に変更する必要がある...(;_;)
    @foo1 = foo1
       :
  end
```

そこで、カスケードパターンの応用です。
new にはブロックを渡させます。そのブロックは new されるオブジェクト自身を渡して呼び出します。
そうすると、ブロックの中でオブジェクトに対して、任意に setter を呼び出してまとめてプロパティの設定を行うことができます。

```rb
# 定義側 ....... プロパティが増えても initialize を変更しないでも、だいたい間に合う
class C
  attr_accessor :foo1, :foo2

  def initialize
    yield(self) if block_given?   # ブロックが与えられた場合のみ実行するため if block_given? をつける
  end
end
```

```rb
# 呼び出し側 ....... 設定したいプロパティは、ブロック内で任意に設定する
c = C.new do |obj|
  obj.foo1 = 'hello'              # 任意の setter を呼び出す
  obj.foo2 = 'world'
end

p c.foo1     #=> "hello"
p c.foo2     #=> "world"
```

# instance_eval を使う
定義する方は幸せになりましたが、呼び出しのブロックで毎回レシーバを指定しているのはちょっとアレです。

instance_eval を使ってみましょう。
instance_eval は与えられたブロックを評価しますが、その際にブロックの self を自分自身にします。

```rb
# 定義側
class C
  attr_accessor :foo1, :foo2

  def initialize(&block)
    instance_eval(&block) if block 
  end
end
```

これで、呼び出し側もすっきりします。(してますよね?)

```rb
c = C.new do
  foo1 = 'hello'
  foo2 = 'world'
end
```

そして確認。

```rb
p c.foo1     #=> nil     # 値が設定されていない!!
p c.foo2     #=> nil     # 値が設定されていない!!
```

。。。。失敗です。値が設定されていません。
これはブロックの中の「foo1 = 'hello'」等が setter 呼び出しでなく、ローカル変数への代入と解釈されたからです。
残念です。

# attr_accesor 代替メソッド
instance_eval は残念でした。。。と、諦めたらそこで試合終了です。

諦めず、attr_accessor の代替メソッドを作りました。名前は property としました。
property は attr_accessor と同じくアクセサを作ります。
アクセサメソッドは getter/setter 兼用で、引数が与えられた場合は setter、与えられない場合は getter として機能します。
setter が以下のように書けて、Ruby にもメソッド呼び出しと解釈されます。

```rb
obj = C.new { foo 'hello' }
```

以下が property の実装です。実装の説明は割愛します。(本題でないし、長くなってきたので。。。)

```rb
module Property
  def property(name)
    module_eval %-
      def #{name}(*arg)
        if arg.empty?
          @#{name}                        # getter
        else
          @#{name} = arg.first            # setter
        end
      end
    -
  end
end
```

使い方の例示も兼ねて、propery を使ってクラス定義してみます。

```rb
# 定義側
class C
  extend Property                    # Propery モジュールを extend する

  property :foo1                     # attr_accessor の代わりに propery を使う
  property :foo2                     #          〃

  def initialize(&block)
    instance_eval(&block) if block 
  end
end
```

呼び出し側も変更します。

```rb
c = C.new do
  foo1 'hello'     # setter 呼び出し: foo1('hello') とも書き換えられる
  foo2 'world'     # setter 呼び出し: foo2('world') とも書き換えられる
end
```

そして確認。

```rb
p c.foo1     #=> "hello"
p c.foo2     #=> "world" 
```

できました。OKです。

# 完成形
instance_eval ではブロックの self がオブジェクト自身になります。
ですが、クロージャを渡したい時など self はそのままの方がいい場合も考えられます。

なので、渡されたブロックに

  - 引数がある場合はオブジェクト自身を引数で渡し
  - 引数が無い場合は instance_eval を使う

ことにします。

以下が、完成形です。

```rb
module Property                          # 前出と同じです
  def property(name)
    module_eval %-
      def #{name}(*arg)
        if arg.empty?
          @#{name}                        # getter
        else
          @#{name} = arg.first            # setter
        end
      end
    -
  end
end
```
```rb
# 定義側
class C
  extend Property                    # Propery モジュールを extend する
  
  property :foo1                     # attr_accessor の代わりに propery を使う
  property :foo2                     #     〃

  def initialize(&block)  
    case block.arity                 # ブロックの引数の数で場合分け
      when 0 ; instance_eval &block
      else   ; yield self
    end if block
  end
end
```

確認です。

```rb
# 引数なしブロックの場合
c1 = C.new do
  foo1 'hello'     # setter 呼び出し: foo1('hello') とも書き換えられる
  foo2 'world'     # setter 呼び出し: foo2('world') とも書き換えられる
end

p c1.foo1     #=> "hello"
p c1.foo2     #=> "world" 
```
```rb
foo1 = 'have'
foo2 = 'fun'

# 引数ありブロックの場合
c2 = C.new do |obj|
  obj.foo1 foo1     # セットしているのは、ブロックの外の foo1
  obj.foo2 foo2     # セットしているのは、ブロックの外の foo2
end

p c2.foo1     #=> "have"
p c2.foo2     #=> "fun"
```
OK です。
ここでおしまいです。

# おわりに
本稿内容の動作確認は以下の環境で行っています。

- Ruby 2.1.5 p273

property と cascade は Gem にしていますが、Gem 版は仕様・実装が異なります。
(2014/12/27現在 テストコード、ドキュメントなど整備できてません)

- [GitHub object_cascadable](https://github.com/lnznt/object_cascadable)
- [RubyGems object_cascadable](http://rubygems.org/gems/object_cascadable)


- [GitHub instance_property](https://github.com/lnznt/instance_property)
- [RubyGems instance_property](http://rubygems.org/gems/instance_property)