以下のようなエントリを見つけました。非常に良い問題意識です。そう、GUI プログラミングは泣けるほど面倒くさいのです。

subtech - Pink Blossom Diary - AS3/Flex2 を使い始めて約半年より

まずイベントドリブンなプログラミングに慣れてないのが一つで。Flex のイベントや自前イベントやをただ単に投げまくってると、とりあえずは動くけど後からメンテし辛いスパゲッティコードができあがる。このスパゲッティコードは goto 文が乱立するコードよりも酷く、goto だったら割と行き先は把握できるけど、イベントを投げまくってるだけだと、どこでどのオブジェクトがこのイベントを受け取るかが解らない。解りづらい。いちいちソースコード grep ですね、おめでたいですね。あのイベントが発生してから、そのイベントが終了したら発生するイベントが終了したらウィンドウ閉じて、その間は別のイベントはブロックして/発生しないようにして、とかもうわけわかんない。これも GUI プログラミングをしたこと無いからのような気もしなくもないけど。


もう、このエントリは素敵すぎです。GUI のいやらしさをよく表現しています。しかし、残念ながら、C# + Windows.Forms を使おうが、Java + Swing を使おうが、Delphi + VCL を使おうが、そのスパゲティから逃れることはできません。

なぜ、C# や Java でもこの問題から逃れることができないのか、考えて見ましょう。確かに、Form や Window を継承したクラスにイベントハンドラを追加していくスタイルは、一見すると、わかりやすくて簡単です。でも、何の指針もなく開発を進めていくと、イベントハンドラに、UI の状態管理、アプリの処理、UI の見た目管理などの処理が混ざることになり、結局、Form や Window を継承したクラスはスパゲティになります。

言い換えると、Web アプリは、MVC2 を採用したフレームワークにしたがって書けば、関心ごとがそこそこ分離され、わりと綺麗に書けます。でも、GUI プログラミングの場合、フレームワークに従っても、複数の関心ごとを分離してはくれません。開発者は自衛する必要があります。また、関心ごとの分離という意味では、JavaScript でも、スタンドアロンアプリでも本質的に同じ問題を抱えています。

では、どうすれば、このいやらしさから逃れられるか考えて見ましょう。

(0) 前提

複雑な GUI アプリの場合のアイディアを書きます。一応、Web(Ajax とか Comet とか絡みそうなもの)を前提にしています。

(1) UI の状態は抽象化しよう

UI の状態を抽象化します。vi みたいにモードを作ってもいいです。

GUI アプリでは、特定の作業をしているときに、特定の入力を排除するようなことがあります。保存中に全ての入力を排除するとかですね。どの状態(モード)のときに、どの View 部品が、どのイベント(キーとかマウスとか)を受け付けるか、わかりやすく定義できるような抽象化が望ましいです。View 部品は構造化しておくと、粒度に応じた管理が出来るようになります。

また、UI の状態遷移図を書いて整理して、状態遷移を実現するフレームワークを書いて抽象化してもいいでしょう。State パターンですかね。サーバと通信があるなら、それも考慮した状態管理を行いましょう。

Kodougu ではこの辺の手を抜いていて、関係線の作成中に Del キーを押したりするとちょっと変な動作をします。

(2) イベントハンドラは UI の状態管理だけを行おう

イベントハンドラにアプリの処理を書かないようにしようということです。画面描画に関わるような処理も書かないようにして、UI の状態管理に徹させることです。イベントハンドラは UI の状態に応じて、別のクラスか関数に書いたアプリの処理を呼び出しましょう。

(3) 必要ならアプリの処理と UI の変更処理は分離しよう

ここで言う UI の変更処理とは、たとえば、チャットの場合、ユーザーの発言を受信したら画面を書き換えたりするようなことです。特定の UI の状態で、ボタンを disabled にするような処理は別です。そういう処理は、状態管理で考えてください。

アプリの処理とは、チャットなら、クライアントで発言ログやログインユーザを管理したりするような処理です。

以下のケースなら、アプリの処理と UI の変更処理の分離を検討した方がいいです。当てはまらない場合は行う必要はありません。
1. アプリの処理と UI の変更処理が一対一でない場合
2. アプリの処理と UI の変更処理が同一のイベントで処理できない場合(画面への反映はサーバの応答待ちとか)
3. サーバの処理とクライアントの処理の境界があいまいな場合(Web なら、HTML の組み立てをサーバでやるか、JavaScript でやるかあいまいなとき)

(4) 必要ならアプリのモデルを定義しよう

Web アプリの場合、サーバにはしっかりとしたモデルクラス(社員クラスとかね)があるかもしれませんが、JavaScript にはそれはないかもしれません。サーバとは異なるモデルかもしれませんが、クライアントにモデルが必要になら、ちゃんと定義しましょう。Kodougu(Web 上で動作するモデリングツール) は、クライアントにもモデルを定義しています。

(5) アプリ処理、画面変更、サーバとの通信の順序を決めよう

そのままです。機能ごとに上記の順序が変わるとわかりにくいので、決めた方がいいです。

とりあえず、こんな感じかなと思います。私はまだまだ、JavaScript については経験不足です。Kodougu も JS の部分は、2000 行位しかなく、小さなものしか作ったことがありません。

■ 追記(2007/8/14 4:53)
GUIプログラミングのパターンが知りたい : akiyan.com

イベントドリブンでステートフルなGUIプログラミングには、いわゆるWebサーバーアプリーケーションでの抽象化経験は全然役に立ってる気がしません。むしろ余計に感じたりします。


無駄になるということは無いと思います。要は関心ごとの分離ですから。ただ、フレームワークを「使う」Web アプリ開発と異なり、複雑な GUI アプリはフレームワークを「作る」ことになるという違いはあります。たしかに、Rails アプリを作るように、複雑な GUI アプリを作ったら自滅しますね。そういう意味では、上記の余計に感じるという表現は非常に示唆に富んだものだと思います。

Posted by あかさた
最近のエントリ
最近の読書メモ