osiire’s blog

ふしぎなそふとやさん

GUIを作るシステム開発がコスト高な理由

※似たようなことをもう何度か書いてきたけど、自分の考えをまとめるために書きます。読みづらいのであしからず。

レイアウトできない問題

 思うようにレイアウトできない。無駄な試行錯誤の時間がかさむ。これはプログラマGUIライブラリへの習熟不足の問題ではない。(そもそも習熟に多大な時間がかかるなら、それはそれで問題。)本質的でない不合理な難しさが多くのGUIライブラリの採用するレイアウトシステムにはある。場合によっては、顧客の望む微妙な配置変更が不可能で、顧客にとって簡単に思えることが実現できないという信用低下すら招く。(「見た目の問題」という言葉の通り、顧客はUI動作は低コストですぐに調整できると信じている場合が多い。)かといって、レイアウトシステムを使わないなどという選択肢はコスト的に論外。
 レイアウトシステムは早急にGridLayoutから脱却し、レイアウトのための述語とその合成手段を用意すべきである。

イベント処理長時間化問題

 イベントハンドラを長時間占有してしまう処理が発生すると、UIが固まる、他の非同期処理(タイマーやIO)が失敗するなどの致命的な悪影響が出る。それを防ぐための場当たり的な対応をすると、不具合の出現がタイミングによるような再現不能&修正困難な状況をつくり出してしまう。逆に長時間になりそうな処理を所構わず非同期化してしまうと、今度は処理の流れが読みにくくなる、同期のためのイベントが増えるなど、可読性の低下、コストの増大を招く。これはイベント処理に単一スレッドを用いるというGUIライブラリの根幹から発生する問題である。
 「イベントドリブン」という実行すべき処理が時系列的に重なる可能性がある実行モデルを実現する自然な手段は、やはりマルチスレッド・マルチプロセスである。古き共有メモリによるマルチスレッド手法が人間の制御できる複雑さを越えている事は明白なので、メッセージパッシングやそれを拡張した仕組みを導入し、自然に複雑さを制御できるようになる事が期待される。

イベントがフラットすぎる問題

 イベントハンドラを登録したイベントに思わぬ発生源があって、それによって不適切な処理が走ってしまう不具合はよく発生する。逆に、意図したイベントが上がらず、不具合になる場合も多い。単にGUIライブラリへの習熟度の問題と言われればその通りだが、そもそもイベントのセマンティクスが理解しづらい、おおざっぱすぎるという事情があるために、多くの人が過ちを繰り返しているとも考えられる。(例えばactivateイベントって何?いつ起こる?)この問題への対応策としては2つが考えられる。

  1. ライブラリが発生させるイベントについては、そのイベントが何が元で発生したイベントなのかが明確に分かるようにしておく。例えば、ユーザーの操作によってそのイベントが発生したのか、プログラム内部が状態を変更したから発生したのか程度は場合分けを行いたい。これがあれば、不要なイベントが不適切な処理を起動することは少なくなる。
  2. イベントを第一級化し、合成できるようにして、イベント内部に構造を導入できるようにする。こうすると、単純なイベントから複雑なイベントを容易に生成できるようになる。これによってGUIライブラリは複雑なセマンティクスを持つイベントをわざわざ用意する必要が無くなり、プログラマはある目的に特化したイベントを柔軟に作成できる。

コンポーネント固すぎ問題

 GUIコンポーネントGUIアプリケーションの開発において大きな役割を担う。特に入力ボックス、コンボボックス、グリッドコントロールは、顧客のデータ入力タスクを効率化するために細かいチューニングが命となる重要部品である。にも係わらず、これらGUIコンポーネントの動きをカスタマイズする手順がとても難しいライブラリが多い。コンポーネントが非常にモノシリックなのだ。パラメーター化された動作は少なく、継承によって僅かに動作を上書きできるのみである。([追記]パラメータ化の代わりに、プロパティという名の大量のフラグで複雑な条件分岐を行う手法が大活躍。これらのプロパティの意味を学習するだけで高コスト。「そんなプロパティがあったのかー」と後から気づいた事が何度もある。それに、フラグ(プロパティ)は互いに独立ではない場合があり、不具合と混乱の元凶になっている。)後は、コンポーネントの本来の動きの後に、コンポーネントの外部から瞬間的に状態を変更し、あたかも動きが変わったように見せかけるしかない。場合によってはそれすら不可能だ。
 GUIコンポーネントは内部状態をモナドのような直接参照・更新できないものとして公開し、描画・状態更新・動きをパラメーター化すべきである。抽象的過ぎて余計不便なら、コンポーネントに定型のパラメーターを適用できる便利な関数をライブラリ側が用意すればよい。これはモノシリックなコンポーネントを解体して組み直す自由を得ることに等しい。