GAE開発の落とし穴
Googleのクラウド環境をつかったGoogle App Engineによる開発するにあたり、初めての試みで苦悩する開発者達の経験をもとに、各開発フェーズにあわせて問題点やどう解決したかをご紹介します
ブックマークされました!
ブックマークがエラーになりました。もう一度お願いします。

作者 Werner Schuster , 翻訳者 近藤 修平 - (株)永和システムマネジメント 投稿日 2008年7月6日
プロパティ、それは次の約束された地だ。少なくとも、この話題で持ちきりなJavaのブログ界隈から目を反らすことができないのであれば、だが。実はプロパティこそが、この世界を救う次世代の技術であり、喉から手が出る程求めていた銀の弾丸を与えてくれるもので、要するに、Java開発者達がJavaを使っていてよかったと実感させるような技術なのだろうか?ふん、プロパティが持つ絶大な力を理論付けるだけでは退屈だ。プロパティが役に立つのかどうか、Rubyに実際に機能を追加してみて、どういう具合になるのかを検証してみるのはどうだろうか?この記事にあるコードを書いたとしても、語彙や文法といった言語空間に悪影響は無いので、心配する必要はないのだから。
どうすればRubyにプロパティを追加できるだろうか?そこで、組み込みDSLの実装に挑戦してみよう。DSLをはじめる一番よい方法は、まず思ったままに書いてみて、それっぽく見えるものを大雑把に掴むことだ。
C#のプロパティをRubyで書いたらこんな感じになるだろうか。
class CruiseShip
property direction
property speed
end
最初の形では、Rubyの正しいコードではないものの、そう遠いわけでもない。このクラスをロードしようとすると、Rubyは"direction"と"speed"が未定義である、というエラーを出す。
"property"の呼び出しでプロパティ名をつけているのを、ロード時に評価されないようにしよう。そう。シンボルを使えばいいんだ!
class CruiseShip
property :direction
property :speed
end
このコードを構文解析してロードしてみた。だが、まだこんなエラーが残っている。NoMethodError: undefined method 'property' for CruiseShip:Class
これを解決するには、簡単な事実に気づけばよい。Rubyのクラスを定義した場合、それは単に宣言しているというだけではなく、クラスがロードされる時に実際に実行しているということなのだ。以下の行がロードされた時に、
property :direction
"property"という関数を探して、":direction"というパラメータとともに呼び出している。この時、"property"メソッドを使えるようにするには、どうすればよいだろうか?そこで、こうしてみよう。
def property(sym)
# do some stuff
end
class CruiseShip
property :direction
property :speed
end
やっと問題なくコードがロードされた。"property"関数の定義をファイルの頭に持ってくるのはあまり良いやり方とは言えないが、それについては後で取り上げることにする。そこで再利用できるような形に修正するつもりだ。
では"property"関数の実装を肉付けしていこう。"property"関数はクラス定義が実行されるときに呼び出されると言ったが、それは取りも直さず、その中でクラスにメソッドを追加することが可能であるということを意味している。これを利用して、クラスの一部として組み込みたいメソッドを定義することができる。"property"関数に以下のコードを加えてみよう。
define_method(sym) do
instance_variable_get("@#{sym}")
end
これは以下のコードをクラスに追加するのと同じ結果をもたらす。
def direction
@direction
end
つまり"direction"プロパティのゲッタとなる。同様にセッタを追加するにはこのようにすればよい。
define_method("#{sym}=") do |value|
instance_variable_set("@#{sym}", value)
end
ただ、これではまだまだ便利なものとは言えない...実は、同じことは、元々Rubyにある機能でattr_accessor :propertyを使えばできてしまうからだ。
なんだって?この記事で取り扱っている内容とこれまで実装してきた機能は、もともとRubyにあったというのか?いや、全てというわけではない。この記事では単にクラスにセッタとゲッタを追加する以上のものをプロパティに求めている。プロパティには、その値が変更された時に通知してくれるようなリスナを登録しておくことができるようになった方がよい。今になって考えてみると、古き良きオブザーバパターンはとても便利なものだった。ただ、それをJavaでやろうとすると、相変わらず膨大な量の退屈な入力作業とおきまりのコードを避けて通ることができない。オブザーバパターンには全てのリスナを呼び出すコードがある。そのコードはいつだって同じだ。だが、リスナに通知を始めるためにはリスナの登録が必要だ。そこにはリスナの登録/抹消をするような追加/削除メソッドがある筈だ。このコードはプロパティ名毎に用意しなければらないもので(add_direction_lisnerが必要だ...)、Javaでは自動的にそういったことを行う手段はない。
だがちょっと待って欲しい。これはRubyだ。メタプログラミングが力になってくれる。メタプログラミングは、コンピュータという怠惰な怠け者を使って、我々の代わりに退屈な仕事をさせるための技術で、これを使えば我々はバラの香りを楽しんだり、猫に餌を与えるための時間を増すことができるという寸法だ。
これはセッタを実装している部分で、すでに存在しているコードだ。
define_method("#{sym}=") do |value|
instance_variable_set("@#{sym}", value)
end
さて、こうしてみても、まだ通知することはできないだろうか?
define_method("#{sym}") do |value|
instance_variable_set("@#{sym}", value)
fire_event_for(sym)
end
ここにはまだリスナを取り扱うメソッドがない。"property"関数でやったように再びdefine_methodのトリックを使ってみよう。以下のように、特定のイベントの為のメソッドを定義する。
define_method("add_#{sym}_listener") do |x|
@listener[sym] << x
end
リスナを削除したり、リスナにアクセスするメソッドは同じようにすればよい。リスナの配列を用意したりだとか、その他の残りのコードは読者の課題としておこう。(文句を言わないように。ほんの数行のコードばかりなのだから。)
このコードがどう動くのか検証しよう。
h = CruiseShip.new
h.add_direction_listener(Listener.new)
h.add_bar_listener lambda {|x| puts "Oy... someone changed the property to #{x}"}
h.bar = 10
これは「おや、誰かがプロパティの値を 10 に変えたようです。」と出力されるだろう。
素晴らしい...そして簡単だ。しかしここでお楽しみの続きをする前に、ちょっとコードを整理してみよう。
さて、どうやったら、このクラスの中にある機能が他でも使えるようになるだろうか?ここでミックスインというとても便利な機能の出番だ。ここで書くのは、クラスの定義にミックスインして使う、実にありきたりなRubyモジュールだ。不思議な感じがするかい?でも、こんなに簡単なんだ。
class Ship
extend Properties
end
こうすれば、Shipクラスの中でPropertiesモジュールの全ての機能が使えるようになる。これは、プロパティ呼び出しを実現する方法としては、なんと簡単な表記だろうか。他の言語では継承、つまり、クラスにメソッドを定義して、ユーザにそのクラスの継承を強制するという手段に訴える必要があったりする。一方ミックスインでは、クラスの継承階層には手を付けず、必要な機能をただ混ぜ合わせる(ミックスイン)するだけで済む(そう!それこそが名前の由来だ!)。
ここ言いたいのは、デモで作ったコードはミックスインを使って括り出せるということだ。
module Properties
def property(sym)
# all the nice code
end
end
こうすれば、以下のようにできる。
class Ship
extend Properties
property :direction
property :speed
end
また、こうすることで、このクラスはPropertiesミックスインを使っているという事実を明示することもできる。これもまたよい効果だ。コードを書いているとき、この拡張についてまだ精通していないならば、Propertiesミックスインのドキュメントを見るか、ソースを見ればよいのだと。
プロパティの基本的で便利な機能を使って、何かおもしろいことをしてみよう。「契約による設計(DbC)」のコンセプトでは、クラスに関するいくつかの制約と不変表明を定義するようになっている。静的言語では最初の取っかかりで次のように書くが、これはint型の値だけが受け入れ可能であることを意味している。
void foo(int x)
もちろん、厳密にint型が何を指しているのかという問題はある。2の31乗という範囲にどんな意味があるというのか?":speed"というプロパティがあって、そこには0..300の値(あなたの持っている平均的な巡航船とは訳が違う)を入れたいとしたら、なおさらだ。他の切り口でこのことについて考えているのが、Gilad Bracha氏の「切替可能な型システム(Pluggable Type System)」という考え方(PDF・英語)で、つまりこれは、不十分な既存の型システムの代わりに、型とその範囲をより宣言的な方法で簡単に自分自身で定義することで、たくさんのif/elseの構文と防御的なプログラミングを使ってコードが雑然としてしまうのを防ぐというものだ。
さて、なぜ「契約による設計」と「切替可能な型システム」の講義をしているのだろうか?そう、我々は既に存在している言語を使っているからだ。プロパティの値についての制約を明確にするような便利機能をそこに追加しようとしている。
ここはひとつ、クリエイティブになって問題をどうやって解決したいのかを決めよう。範囲を使うのも、なんらかの指定した型を使うこともできる。ブロックを放り込んで使うことも出来る。
property(:speed) {|v| (v >= 0) && (v < 300) }
実装はこんなに簡単だ。
def property(x, &predicate)
define_method("#{sym}=") do |arg|
if(predicate)
if !predicate.call(arg)
return
end
end
instance_variable_set("@#{sym}", arg)
fire_event_for(sym)
end
end
このコードは、値の範囲を決めたり、型やnilのチェックをするような雑然としたコードは排除した上で、プロパティの値はクラスの内部に定義された範囲の中に常に収まる、という良い副作用はそのまま残している。見通しのよい一つの場所で、プロパティの全ての明確な制約を定義し、かつ、そこで完結している。
実際のところ、"speed"プロパティの制約については、もっと簡潔な方法で定義することができる。この記事の対象からは外れるが、課題として、以下のような感じで書けるように実装してはどうだろうか。
property :speed, in(0..300)
ただ、これは正しくないRubyコードだ("in"というのはRubyキーワードだから)。まずコードを見える形にして、それをちゃんとしたRubyのコードに作り替えていく、というやり方から始めるのは効果的だと思う。
楽しんで欲しい。
確かに、後半ではプロパティの機能に何か追加するような話ではなかったが、プロパティとその通知の仕組みは、こういった型付けの話には関係していない方が良いだろうと単純に思ったからだ。とは言えRubyを使っているのだから、鋳型に流しこむように、あなたが望むものに対して言語を合わせてゆくことができる。この記事でやってきたのは、Rubyでできることの一例にしか過ぎない。
組み込みDSLに対する反論は多岐にわたっている。組み込みDSLがコードを読みにくくしているという意見もある。それは事実でもある。ちょうど、"print(x)"というコードが読みにくいのと同じように。この関数の呼び出しが何をしているのかは、一体どうやったら知ることができるだろうか?結局、ドキュメントを読むか、ソースコードを眺めるぐらいしかできない。でもそれは、DSLを実装するのと何か違うのだろうか?違いは無い。
ここで使った技法はとても単純なもので、「ポリモーフィズム」や「再帰的な型の実現」(や今や市民権のあるアメリカ生まれのいろんな用語ならなんでも)といったキーワードに怯えることのない開発者であれば、全員がすぐ理解できるようなもので、これらの技法が使えるようになると、ずっと簡潔で適切なメンテナンス可能なコードが書けるようになる。
原文はこちらです:http://www.infoq.com/articles/properties-metaprogramming
(このArticleは2007年4月18日に原文が掲載されました)
Googleのクラウド環境をつかったGoogle App Engineによる開発するにあたり、初めての試みで苦悩する開発者達の経験をもとに、各開発フェーズにあわせて問題点やどう解決したかをご紹介します
去る1月12日、定理証明支援系ツールCoqの初心者向けチュートリアルが開催さ れた(http://kokucheese.com/event/index/23667/)。今後も2月2日 (http://kokucheese.com/event/index/23744/)、2月9日、2月16日と引き続き開 催されていく予定である。本記事では、開催の様子をレポートする。
Neal Gafter氏はOracleによるJava買収の影響に関する議論、Javaにセグメンテッドスタックやメタオブジェクトプロトコルを追加することについての主張、そしてJavaとC#との比較について話をしてくれた。
GoogleはVMをともなう新しい言語であり、JSコンパイラでもあるDartをプレビューした。 InfoQはDartのアプリの構築に貢献する文法の裏側を探った:スナップショット、Isolate、モジュール方式
本記事ではCSPベースの「マルチドメイン・モデル検査ツール」である、PAT(Process Analysis Toolkit)について紹介する。モデル検査は、形式手法(Formal Method)という方法論を基礎とする技術であり、複雑さが増大しながらも安全性を求められる、現在のソフトウェア開発の状況に対する処方箋の1つとして注目されている手法である。
前回まで、Jenkinsの幾つかの側面に注目して解説をしてきました。シリーズ最後の今回は、Jenkinsをサービスとして使う方法を紹介します。
Alloyは、MITにて開発された仕様記述言語であり、ツールによる自動解析を使い、インクリメンタルに形式仕様が書けることが特長である。筆者らはAlloy開発者による、Alloyを使った形式手法入門書を翻訳、今夏にオーム社より刊行した。本記事では、Alloyの簡単な概要と、翻訳書『抽象によるソフトウェア設計』(「Alloy本」)を紹介する。
スマートフォンを中心としたマルチデバイスにおけるタッチユーザーインターフェイスへの対応は、既に必須の項目となりつつある。本記事では、Windows デバイスにおける UX のベースとなっている「メトロ」というデザイン言語を掘り下げながら、既存環境を意識しつつもどのようにタッチユーザーインターフェイス開発に取り組んでいくべきであるかについて解説していく。
No comments
スレッド表示 返信