explicit DSLとimplicit DSL
先輩のブックマーク経由で.
http://www.infoq.com/news/2008/02/presentation-ruby-art-of-dsl
explicitだと、通常のブロックを使う。storeの記述が重複して単調.
when_shopping do |store| store.buy :popcorn store.buy :soda, @soda_amount end
implicitだと、単調だったstoreの箇所を削り、よりコンパクトになる.
これならドメインエキスパートにもフレンドリーな表記♪
when_shopping { buy :popcorn buy :soda }
どうやったら、Rubyで実行可能にできるのかなと思っていたら
http://www.infoq.com/presentations/kilmer-ruby-dsls
で解説してあった.instance_evalを使うんだね.
class ShoppingTrip module Behavior def when_shopping(&block) ShoppingTrip.new(&block) end end def initialize(&block) @items = [] instance_eval(&block) end def buy(item, count=1) @items << [item, count] end def show @items.each { |item| puts "item_name=>#{item[0]}, quantity=>#{item[1]}" } end end extend ShoppingTrip::Behavior quantity_a = 2 @quantity_b = 3 when_shopping { buy :popcorn, quantity_a # 2は入る buy :soda, @quantity_b # 3は入らない }.show
ただし、上記の記述では、sodaの数は3にならない.
これは、instance_eval経由で実行されるbuyメソッドは、ShoppingTripオブジェクト側のスコープで評価するが、このスコープでは、インスタンス変数の@quantity_bに値が入っていないため.(たぶん
explicitアプローチなら、期待通りsodaの数は3になる.
popcornの数が2になる理由は、私には説明できないw.
ここら辺が、トレードオフか.