メソッド名とシグニチャを統一して美しいAPI設計

はてなブックマークに追加はてなブックマーク Yahoo!ブックマークに登録 ニフティクリップに追加 Livedoor クリップに追加 BuzzurlにブックマークBuzzurlにブックマーク Twitterに投稿  

=追記(2010/07/23)
Rubyベストプラクティスのひとり読書。理解が難しかった部分を中心にまとめています。
書籍から引用しすぎと思われたページ、理解したページなどを削除して、当初より公開ページを減らしました。すいません。
読書のまとめ記事は難しいと感じたので、今後はブログで読書のまとめは書かないでおきます。差し障りがあれば現存するページも削除致しますので予めご了承下さい。
=追記ここまで


P36からのRuportライブラリを使って、Tableオブジェクトを作成する例。
あらかじめ、以下を実行してRuportをインストールしておく。

gem install ruport
gem install ruport-util

また、people.csv を使うのであらかじめ同じディレクトリに作成しとく。


メソッド名とシグニチャを統一して使いやすAPIにする

受け取るオブジェクトによって異なるメソッド名とシグニチャのAPI。
これだと使いにくAPI。

table1 = Ruport::Data::Table.new(
  :column_names => %w[first_name last_name],
  :data         => [["Gregory", "Brown"], ["Deborah", "Orlando"]] )
 
table2 = Ruport::Data::Table.load("people.csv")
 
csv = "first_name,last_name\nGregory,Brown\nDeborah\nOrlando\n"
table3 = Ruport::Data::Table.parse(csv)

上のnew, load, parseメソッドをラップして、Tableメソッド1つのAPIにする。
こうすると使いやすい(覚えやすい)APIができる。

table1 = Table(%w[first_name last_name],
  :data => [["Gregory", "Brown"], ["Deborah", "Orlando"]] )
 
# CSVファイルを受け取る args[0] => .csvファイル
table2 = Table("people.csv")
 
# 文字列を受け取る args[0] => Hash (:string => hoge)
csv = "first_name,last_name\nGregory,Brown\nDeborah\nOrlando\n"
table3 = Table(:string => csv)


Tableメソッド(使いやすいAPI)の実装とサンプル実行

require "pp"
require "rubygems"
require "ruport"
 
def Table(*args, &block)
  table = case args[0]
          when Array
            opts = args[1] || {}
            Ruport::Data::Table.new( {:column_names => args[0]}.merge(opts), &block)
          when /\.csv$/i
            Ruport::Data::Table.load(args[0], &block)
          when Hash
            if file = args[0].delete(:file)
              Ruport::Data::Table.load(file, args[0], &block)
            elsif string = args[0].delete(:string)
              Ruport::Data::Table.parse(string, args[0], &block)
            else
              Ruport::Data::Table.new(args[0], &block)
            end
          else
            Ruport::Data::Table.new(:data => [], :column_names => args, &block)
          end
  return table
end
 
# カラム名(配列)とデータ(ハッシュ)を受け取る args[0] => Array
table1 = Table(%w[first_name last_name],
               :data => [["Gregory", "Brown"], ["Deborah", "Orlando"]] )
pp table1
 
# CSVファイルを受け取る args[0] => .csvファイル
table2 = Table("people.csv")
pp table2
 
# 文字列を受け取る args[0] => Hash (:string => hoge)
csv = "first_name,last_name\nGregory,Brown\nDeborah,Orlando\n"
table3 = Table(:string => csv)
pp table3


実行結果

>ruby 36.rb
#<Ruport::Data::Table:0x2c2ff68
 @column_names=["first_name", "last_name"],
 @data=
  [#<Ruport::Data::Record:0x2c2f34c
    @attributes=["first_name", "last_name"],
    @data={"first_name"=>"Gregory", "last_name"=>"Brown"}>,
   #<Ruport::Data::Record:0x2c2e7e4
    @attributes=["first_name", "last_name"],
    @data={"first_name"=>"Deborah", "last_name"=>"Orlando"}>],
 @record_class="Ruport::Data::Record">
#<Ruport::Data::Table:0x2d48170
 @column_names=["first_name", "last_name"],
 @data=
  [#<Ruport::Data::Record:0x2d47590
    @attributes=["first_name", "last_name"],
    @data={"first_name"=>"Gregory", "last_name"=>"Brown"}>,
   #<Ruport::Data::Record:0x2d4707c
    @attributes=["first_name", "last_name"],
    @data={"first_name"=>"Deborah", "last_name"=>"Orlando"}>],
 @record_class="Ruport::Data::Record">
#<Ruport::Data::Table:0x2d42b58
 @column_names=["first_name", "last_name"],
 @data=
  [#<Ruport::Data::Record:0x2d41fdc
    @attributes=["first_name", "last_name"],
    @data={"first_name"=>"Gregory", "last_name"=>"Brown"}>,
   #<Ruport::Data::Record:0x2d41ac8
    @attributes=["first_name", "last_name"],
    @data={"first_name"=>"Deborah", "last_name"=>"Orlando"}>],
 @record_class="Ruport::Data::Record">

統一したAPIであるTableメソッドで、違う種類のオブジェクトを受け取り、同一のTableオブジェクトが生成されていることを確認できました。


使われているテクニックのまとめ

デフォルト値付きの引数
ハッシュを用いた擬似キーワード引数
def hoge(*args); fuga; end と*argsで可変長引数
コードブロックの引数(詳細は次回)


日時: 2010年06月23日 11:14
コメントを投稿






トラックバック

■この記事のトラックバックURL:
http://www.mapee.jp/mpe334/mt-tb.cgi/529

この記事にトラックバックされる方は、参照先が分かるようにするために、「メソッド名とシグニチャを統一して美しいAPI設計」へのリンクをお願いいたします。
以下のHTMLタグをトラックバック送信元ページ内に挿入して下さい。



※この記事へのリンクがない、また関連のないページからのトラックバックは反映されませんので、ご了承下さい。






あわせて読みたいブログパーツ
フィードメーター - Ruby勉強ルーム