メソッドの移譲について学んだのでメモです。
参考
実行環境
実装 Forwardable を使っていないパターン test3.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 require 'ostruct' class Stock attr_reader :items def initialize (items ) @items = items | | [] end def last_ones items.select{|i | i.count == 1 } end end item1 = OpenStruct .new({ name: "saba" , count: 3 }) item2 = OpenStruct .new({ name: "maguro" , count: 1 }) def format_item (i ) = "#{i.name} :#{i.count} 個" stock = Stock .new([item1, item2]) stock.items.each{|i | puts format_item(i) } stock.last_ones.each{|i | puts format_item(i) }
Forwardable を使ってみる test2.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 require 'forwardable' require 'ostruct' class Stock extend Forwardable def_delegators : @items , :each def initialize (items ) @items = items | | [] end def last_ones @items .select{|i | i.count == 1 } end end item1 = OpenStruct .new({ name: "saba" , count: 3 }) item2 = OpenStruct .new({ name: "maguro" , count: 1 }) def format_item (i ) = "#{i.name} :#{i.count} 個" stock = Stock .new([item1, item2]) stock.each{|i | puts format_item(i) } stock.last_ones.each{|i | puts format_item(i) }
Stack
に対して、each
を呼び出すと応答するオブジェクトが @items
に代わっている。
変更前は、items
に問い合わせる必要がある。(ということを知っている必要がある。)
1 stock.items.each{|i | puts format_item(i) }
変更後は、インスタンスに直接問い合わせるだけで OK。
1 stock.each{|i | puts format_item(i) }
Enumerable も使ってみる Enumerable は、繰り返し処理を行うための Mix-in だそう。 これを使うと、last_ones が、もっとシンプルになる。
test3.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 require 'forwardable' require 'ostruct' class Stock extend Forwardable def_delegators : @items , :each include Enumerable def initialize (items ) @items = items | | [] end def last_ones select{|i | i.count == 1 } end end item1 = OpenStruct .new({ name: "saba" , count: 3 }) item2 = OpenStruct .new({ name: "maguro" , count: 1 }) def format_item (i ) = "#{i.name} :#{i.count} 個" stock = Stock .new([item1, item2]) stock.each{|i | puts format_item(i) } stock.last_ones.each{|i | puts format_item(i) }
Stock
クラスに、Enumerable
を読みこむことでselect
メソッドを備えることができるようになりました。Enumerable
は、クラスが each
メソッドを備えることが必要であるので、ほとんどForwardable
でeach
を移譲するのがセットの運用と考えられそう。 (each
メソッドを別途作っている記事を見かけたので、またあとで試そうかと。)
ファクトリパターンを作ってみる。 これらを使ってファクトリパターンの実装を試してみます。
test4.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 require 'forwardable' require 'ostruct' class Stock extend Forwardable def_delegators : @items , :each , :push include Enumerable def initialize (items ) @items = items | | [] end def last_ones select{|i | i.count == 1 } end end module StockFactory def self .build(config) items = config.collect {|c | OpenStruct .new({ name: c[0 ], count: c[1 ] })} Stock .new(items) end end def format_item (i ) = "#{i.name} :#{i.count} 個" config = [["saba" ,3 ], ["maguro" , 1 ]] add_item = OpenStruct .new({ name: "tara" , count: 4 }) stock = StockFactory .build(config) stock.each{|i | puts format_item(i) } stock.last_ones.each{|i | puts format_item(i) } stock.push(add_item) stock.each{|i | puts format_item(i) }
ファクトリーパターンの適用とともに、push
メソッドも移譲をしてみました。
今回は、Forwardable モジュールでのメソッド移譲を手始めに、ファクトリーパターンの実装を試しました。 クラスに、直接each
といったメソッドを移譲させられると、中のデータをオブジェクトの外に晒さなくて良くなるのは利点と感じます。 また関連のメソッドチェーンにもつなぎやすいと感じます。
今回、Forwardable や Enumerable を試しましたが、標準で用意されているライブラリについての認識持っておくのって絶対大事ですね。
ではでは。