イベントプログラミングが嫌いだ
オープン系の仕事をするようになって、最初にイノベィティブと感じたのがVisualBasicで提示されたプログラミングスタイルだった。
ツールバーからコントロールをドラッグしてフォーム上に貼り付ける。そしてそのコントロールに用意されているイベントに対して処理を書く。簡単だ〜すげ〜 と。
その後仕事ではDelphiも使うようになって更にイベントプログラミングは推進された。
しかし、現在の私はイベントプログラミングが嫌いだ。語弊を招くけど、正確にはなんでもイベントの中にがりがりと詰め込むスタイルのプログラミングが嫌いだ。
最近でこそ「リファクタリング」の啓蒙により多少まともにはなったけど、得てして経験の
無いプログラマや先見の無いプログラマは、とにかくイベントの中に長々とロジックを書く。それも同じようなロジックを、平気で違うイベントの中にコピペしたり書いたりする。
汎用機時代のプログラマが、VisualBasicで書いたコードは論理が連続していないのでレビューできないと言い放ったけど、VisualBasicで育った世代はデバッグ実行モードで追えるから問題無いと言う。どちらも正しいけれどどちらも後ろ向きな感じがする。
そんな風に考えている中で、現在開発中のフレームワークではイベントプログラミングをできるだけ整然と、無駄無く書けるような仕組みをまずは一つ導入することが目的だ。
ヒントはDelphiに添付されていたアクションリストコンポーネントだった(懐かしいね)。このコンポーネントは、必要とされる動作をアクションという単位で定義しそれを管理するアクションリストに登録し、GUIコントロールはイベントでの処理を書くのではなく、結びつけられたアクションに委譲する。
Gofのデザインパターンを知っているプログラマであれば、これはMediatorのGUIでの実装の一つと書いたほうがピンとくるかも。アクションリストがMediator、処理を委譲してくる
コントロールがColleagueと考えると、どんなものか想像がつくだろうと思う。
汚くなりがちなイベントハンドラをある程度すっきりさせてくれるこのコンポーネントは、なぜか.NETでは標準で用意されていないけど、サードパーティではちゃんと出していたり
する。
現在書いているフレームワークでは、この「アクション」をGUIプログラミング時の制御の
単位として使用する予定。インタフェースはこんな感じに。
public interface IAction { // アクションを定義する一意のID string Id { get; set; } // アクションに関連づけるイメージ Image Image { get; set; } // アクションが示すテキスト string Text { get; set; } // アクションに関連づけるショートカット Keys ShortcutKey { get; set; } // アクションに関連づけるツールチップテキスト string ToolTipText { get; set; } //実行される処理 void Run(); }
アクションリストに相当する概念は考えていない、というのも、今回はGUIが主体では無くアクションが主体だから。それにアクションリストに処理を集中させると、今度はそこがロジックの溜まり場になる事が多いのが、経験上解っているので、今回はGUIがアクションに対して処理を委譲するのではなく、アクションが自らGUIに結びつくのだからMediatorにお伺いを立てているわけではない。うーん、もうMediator(調停者)とは言えないな。(でもなんと表現して良いか解らない自分が情けない...)
.NETのデリゲートを使えばIActionのRun()メソッドを、特定のGUIイベントに結びつけるのは
簡単だ。C#2.0なら匿名デリゲートによりハンドラメソッドの追加さえいらない。
bindControl.Click += delegate(object sender, EventArgs args) { action.Run(); }
JavaのプログラマでEclipse、それもJFaceを知っている方であれば、このようなIActionインタフェースがある事をご存じだろうと思う。実は現在こそ.NETで書いているけど、半年前まではこのフレームワークはJavaで書く予定だったのだ。その時にEclipseのJDTで採用されているJFaceというフレームワークがいたく気に入り、その中で使用されている同名のインタフェースを.NETで開発する事が決まった後も使うことにしたのだった。
アプリケーションプログラマはまずGUIでの処理を検討し、GUIの動作(クリック等)でどのような処理を駆動するのかを決める。その際にその処理を識別する名前、表明するテキスト、起動時に使用するWindowsショートカットキー、マウスを当てた時等に、表示されるツールチップを決定し、実際に駆動される処理をIActionインタフェースの実装クラスとして起こす。
フレームワークはなんらかの設定情報を頼りに、このIAction実装クラスのインスタンスを実行時に任意のGUIに結びつける。任意のGUIに結びつける、という部分がミソで任意、つまり一つのアクションを全てのGUIのクリックイベントに結びつけたって良いのだ。そう、一番狙いたかったのは、イベントに書くコードを決して重複させない事とイベントで実行する処理の粒度を、きちんとプログラマに考えて欲しい事だ。
設定情報はなんでもいいけれど、最初の候補はさんざん日記で書いてきたApp.configだろう。プロパティの数を考えると結構な記述量になる可能性があるので、他の.configファイルとして別ファイルにしたりManifestResourceにしても良いだろう。結びつけられたGUI、つまりWindowsFormsコントロールは、結びついたIActionインタフェースのイメージをボタンに表示したりツールチップを表示したりする事となり、特定のイベント(殆どがクリックだろうけど)によりIAction#Run()が実行される。
公開されているプロパティは、DIにより生成時に依存性として注入する事も可能だ。結びつけるGUIは今のところメニュー、コンテキストメニュー、ツールバーのボタンや、一般的な意思決定用のボタンを考えているんだけど、UIの統一感を出す必要があるのでメニューとコンテキストメニュー、ツールバーに関してはIActionに結びつけるか否かの選択をさせるだけでも、良いような気がしている。