Rubyの委譲標準ライブラリのまとめ

Rubyで委譲を実現するために、大きく二つなライブラリを使っています。

- **forwardable**
- **delegate**

この二つのライブラリはすごく便利ですが、どう使うかと、どの場面に使えばいいかをかなり忘れがちなので、簡単まとめたいと思います。
まずはforwardableからまとめます。

##Forwardable
###使い方

####1.クラス定義で使う
[Ruby magazine](http://magazine.rubyist.net/?0012-BundledLibraries)によると、Forwardableライブラリは **「明示的に指定したメソッドだけを委譲する」**。これはどういう意味でしょう？簡単にいうと、forwardableライブラリは以下の二つのメソッドがポイントです：

- def_delegator
- def_delegators

この二つのメソッドはクラスメソッドであり、使いたいクラスに ```extend Forwardable```で拡張するとこの二つのメソッドが使えるようになります。この二つのメソッドは共通点は、**委譲元のメソッドを、他のオブジェクトに任せる**という点です。例えば

```rb
class Bar
  def convenient_method; p "xxx"; end
  def convenient_method2; p "xxx"; end
  def convenient_method3; p "xxx"; end
end

class Foo
  extend Forwardable
  def initialize; @q ||= Bar.new ; end
  def_delegator :@q, :convenient_method, :same_convinent_method
  def_delegators :@q, :convenient_method, :convenient_method2, :convenient_method3 
end

Foo.new.same_convinent_method #xxx
```
この例を見ればだいたいの使い方が分かると思います：

- def_delegatorはおそらく ```alias```見たいな感じの使い方で、三つのパラメータを受け取り、一つ名は「***任せたいオブジェクト***」、二つ名は「***任せたいオブジェクトの使いたいメソッド***」、三つ名は「***任せたいオブジェクトのメソッドを使って、どの名で呼び出されたい***」となります
- def_delegatorsは配列を受け取り、def_delegatorと違って別名でメソッドを利用するではなく、そのままのメソッド名で任せたいという意味で、任せたいメソッド配列を受け取る。

####2.インスタンスオブジェクトで使う
上の使い方は一つのクラスに対して委譲するとなります。直、いまいち使うインスタンスしか任せたい場合は、どうすればよいでしょう？そのときは ***SingleForwardable*** を使えばよいです。

```rb
printer = String.new
printer.extend SingleForwardable        # prepare object for delegation
printer.def_delegator "STDOUT", "puts"  # add delegation for STDOUT.puts()
printer.puts "Howdy!"
```

使い方はインスタンスオブジェクトを直接に SingleForwardableを拡張すればよいです。拡張した後はそのオブジェクトが```def_delegator```と```def_delegators```が使え、上のようにどれかのオブジェクトに任せたいメソッドを定義すれば委譲することができます。

###使う場面
Forwardableモジュールを、いつ、どの場面で使えばよいかを結構不思議に思う方がいらっしゃるとおもいます。私は個人的に、Forwardableを使って簡潔にオブジェクトの***部品化***ができると思います。
例えば

--------------
「パソコン」というオブジェクト設計したい。
パソコンの機能は、
　- タイピング
　- 画像見える
見たいな機能を搭載した。
インターフェスは以下のように設計したい。

```rb
 Computer.new.type
 Computer.new.view_screen
```

だけど、すでにKeyboardとScreenというクラスが存在し、すでに`Keyboard#type`と`Screen#view_screen`も存在するので、KeyboardとScreenをComputerの部品として使って、typeとview_screenを委譲して使えばコードが奇麗に見えるでしょう。

```rb
class Computer
  extend Forwardable
  def keyboard; @keyboard ||= Keyboard.new; end
  def screen; @screen ||= Screen.new; end

  def_delegators :@keyboard, :type
  def_delegators :@screen; :view_screen
end
```
まとめると、私が個人的に、一つのオブジェクトが複数のオブジェクト（機能）から生成されるとき、Forwardableがとても便利です。また、ほとんどのケースは `extend Forwardable`を使います、SingleForwardableは個人的にほとんど使っていません。

##Delegate
###使い方
DelegateはForwardableと同じ、委譲として使いますが、違う点としてはForwardableが`def_delegator`か`def_delegators`を使って、**明示的**に、どのメソッドを任せたいを使うが、Delegateはクラスにほぼ全てのメソッドを任せたいという意味です。
Delegateライブラリは主に二つの使い方があります

- DelegateClass()
- SimpleDelegator

####1. DelegateClass()
DelegateClassの使い方は、クラスを引数として受け取り、そのクラスのオブジェクトにインスタンスメソッドを委譲するという感じです。
例えば

```rb
class Foo < DelegateClass(Array)
end
```
こう定義すると、FooがArrayのメソッドを使えるようになります。

```rb
Foo.new.push 25
```
ここで、それならFooクラスがArrayを継承すればいいじゃない、の鋭い読者がいるでしょう。確かにArrayを継承するのも、「メソッドを使える」という目的を果たせますが、問題はオブジェクト指向の観点から、**FooはArrayの子クラスではない**、と意味したい場合は、無理矢理Arrayを継承するのがとってもまずいので、その時は DelegateClassの最高さが分かるでしょう。

DelegateClassを使って、委譲先のクラスのインスタンスメソッドを使えるだけではなく、`__setobj__`というメソッドを使って、***メソッドの受け取りオブジェクトを実行時に変える***こともできます。__setobj__について、下の使う場面でもうちょっと詳しく説明します。


####2. SimpleDelegator
SimpleDelegatorの使い方はすごく簡単で、SimpleDelegator.new の引数に渡したオブジェクトのすべてのメソッドが委譲されます。以下の例を見れば使い方が分かると思います。

```rb
class Foo; def i_am_awesome; p "x"; end

bar = SimpleDelegator.new(Foo.new)
bar.i_am_awesome #x
```
この例では、barがFooのインスタンスメソッドをすべて Foo.newで作られたオブジェクトに任せるという意味です。
また、`bar.__setobj__` を使って、委譲先のインスタンスオブジェクトを変えることもできます。

###使う場面
Delegateライブラリの素晴らしさは`__setobj__`に存在します。何が素晴らしいかというと、`__setobj__`でオブジェクトを変えるために、***柔軟にロジックをスイッチする***ことができます。以下の例をご参照ください。

```rb
require 'delegate'


class TicketSeller
  def sellTicket()
    return 'Here is a ticket'
  end
end


class NoTicketSeller
  def sellTicket()
    "Sorry-come back tomorrow"
   end
end


class TicketOffice < SimpleDelegator
  def initialize
    @seller = TicketSeller.new
    @noseller = NoTicketSeller.new
    super(@seller)
  end
  def allowSales(allow = true)
    __setobj__(allow ? @seller : @noseller)
    allow
  end
end

to = TicketOffice.new
to.sellTicket   »   "Here is a ticket"
to.allowSales(false)    »   false
to.sellTicket   »   "Sorry-come back tomorrow"
to.allowSales(true)     »   true
to.sellTicket   »   "Here is a ticket"
```

この例では、一つの`TicketOffice`オブジェクトの#sellTicketメソッドに、二つのロジックを実現した：「チケットが売り切れ」と「チケットがある」。もちろんallowSalesのなかで `if else`で実現するのも可能ですが、ロジックがややこしくなる場合は、別のオブジェクトに分けたいでしょう。そのときはDelegateを使えば柔軟にロジックの切り替えができると思います。

また、Delegateを使って、Strategyのデザインパターンも簡潔に実装できるて考えています。

#まとめ
この記事ではRubyで委譲の使い方・使う場面を簡単にまとめました。
私は誤解、間違うところがあれば、是非気軽にコメントいただけると幸いです。

参考

- http://pivotallabs.com/delegateclass-rocks-my-world/
- http://stackoverflow.com/questions/13104942/when-to-use-ruby-delegateclass-instead-of-simpledelegator-delegateclass-method
- http://wondible.com/2012/01/06/a-delegate-matter/
- http://magazine.rubyist.net/?0012-BundledLibraries