移譲
module Forwardableを使います。
ForwardableはRubyの標準ライブラリです。
オブジェクトの機能を再利用する手法の一つとして、Ruby では言語仕様としてクラスの継承とモジュールの Mix-in を提供しています。これらは、元になるクラスやモジュールの実装までもをそのまま取り込んでしまいますが、他の手段で機能の再利用を実現する手法として、委譲があります。
委譲では、再利用したい機能を自分に取り込むのではなく、その機能を持つオブジェクトに処理を依頼します。
Ruby では特に言語仕様として委譲がサポートされているわけではありませんが、委譲を実現するためのライブラリとして forwardableとdelegate が用意されています。具体的には、これらのライブラリを使用することによって、あるメソッド呼び出しを他のオブジェク>トのメソッドにたらい回すということを簡単に記述することができます。
インスタンスメソッドに移譲
class Outer
extend Forwardable
def_delegators :@inner, :hello
def initialize
@inner = Inner.new
end
class Inner
extend Forwardable
def_delegators :hello
def hello
puts "hello, world!!"
end
end
end
obj = Outer.new
obj.hello #=> "hello, world!!"
Outer::InnerのインスタンスメソッドをOuterのインスタンスメソッドに移譲したので、どちらもインスタンスメソッドです。
Outer.instance_methods(false)
#=> [:hello]
Outer::Inner.instance_methods(false)
#=> [:hello]
クラスメソッドに移譲
以下のサンプルでは、Innerクラスの中身は全く変わっていないことに注目してください。
require 'forwardable'
class Outer
class << self
extend Forwardable
def_delegators :inner, :hello
def inner
Inner.new
end
end
class Inner
extend Forwardable
def_delegators :hello
def hello
puts "hello, world!!"
end
end
end
Outer.hello #=> hello, world!!
Innerクラスのhelloメソッドはプライベートメソッドですが、Outerクラスではクラスメソッドになっています。
Outer.methods(false)
#=> [:inner, :hello]
Outer::Inner.methods(false)
#=> []
Outer::Inner.instance_methods(false)
#=> [:hello]
さいごに
gimeiのコードを追っていて、どうしてinclude
やextend
を普通に使わないのだろう?
と思ったのがこの記事を書くきっかけとなりました。