フィンローダのあっぱれご意見番 第136回「分ける」
← 前のをみる | 「フィンローダのあっぱれご意見番」一覧 | 次のをみる →
今年は異常気象だ、 というセリフを毎年聞いているような気がするのだが、 何をもって異常というのかというと、 直感的に「これはおかしい」ということで判断しているような気がする。 平均値とか標準偏差とかを基準にするわけでもなく、 はなはだ主観的かもしれないが、 今年みたいな状況だと誰が見ても異常気象だというか、 統計的に計算しても、多分異常になるのだと思う。 最新は長距離列車の座席指定もインターネットからできるようになっている。 便利な世の中だと思ったら、 今年は盆休みの週にまず台風がやってきて大変なことになった。 乗るはずの列車が運休になってしまったのだ。 ただ、列車が動いているのか止まっているのか、ということもインターネットから調べることができるから、 そう考えれば、世の中便利になったものだ。 とにかく、予約していた切符が無効になってしまったので、 翌日の座席指定を取り直すことにした。 その前に、指定席が空いているかどうか気になるのだが、 もちろん、指定席の空き状況もインターネットで調べることができるのだ。 実に便利だ。 と思ったら、 世の中それほどIT革命していないという現実が猛烈にわかる出来事に遭遇してしまった。 指定席の状況を調べるためのサイトに接続して、 路線とか区間を指定する、というだけなのだが、 それはもう恐ろしい話になるのだ。 在来線の場合、区間を指定するのに別ウィンドウが出てくる。 それだけでもヤな予感がするのだが、 駅名選択のボタンを押すと表示される別ウィンドウのデザインが、 正直言って訳が分からない。 | ||
まずこのウィンドウが表示された時点で、 「あ」で始まる駅名がセレクタで選べるようになっている。 私が知りたい列車の始発駅は「あ」ではなく「さ」行にあったので、 「さ」をクリックした。 実はこれがまたひどいUIで、 「さ」をクリックできるというのがパッと見ただけでは絶対に分からない隠しボタンのようなデザインになっている。 偶然クリックできることに気付いた人だけが駅名を選択することができるのだが、 もちろん私はその程度の罠にはハマらないわけで、 サクッと「さ」をクリックした。 | ※ 画面を見ただけで、 どこがクリックできるか判別できなければならない。 UI設計の基本である。 例えば、 クリックできる文字の上にマウスカーソルを合わせると太字になる、 というようなUIは、減点対象である。 ただ、この例の場合は、それすらもなかったので、 本当に押してみるまで分からない。 | |
反応がない。 「さ」をクリックしたらセレクタに「さ」行の駅名が出てくると思ったのだが、 いや、実際出てくるのだが、それは2~3分経過してからなのだ。 唖然としていても仕方ないから、 次に目的地を選択することにした。 今度は「か」行なのだ。 まさかとは思ったが、「か」行を押したら案の定、2~3分待たないと駅名が表示されない。 一体どういうことなのかと思ったのだが、 とりあえず表示されたので選択した。 これがまた無茶苦茶たくさん駅名がある中から探すので面倒なことこの上ない。 駅名は両方とも分かっているのだから、 漢字で入力させてくれればよさそうなものだが、 それすらできないという全世紀的なUIなのである。 | ||
ここでふと画面を見ると、 「乗車駅」「降車駅」というラジオボタンの「乗車駅」がアクティブになっていた。 なぜかよく分からないが、無意識にここで「乗車駅」のラジオボタンを押してしまったのである。 既に「乗車駅」は選択してあったのだが、 このラジオボタンを押した瞬間に、選択されていた駅名がクリアされてしまったのだ。 こういうのは、次に駅名を指定するまで残しておくのが普通じゃないのですか? | ※ しかも「戻る」ことができない。 | |
まあいい、もう一度指定したらいいのだろう。 ということで「さ」のボタンを押す。 当然、2~3分、戻ってこない。 そう何度も失敗するわけにはいかない。 今度は慎重に。 他のボタンは何も触らずに「選択」というボタンを押す。 押した。 何も起きないが…。 しばらく待たされた後、ブラウザにこんな感じの表示が出てきた。 「現在、システムは大変混雑しております。…」 これを見た時、ブチ切れるというよりは、 こういうサイトが実在するということ自体が異常じゃないかと、 ふと思ったりした。 § | ||
一般的に、 ユーザーにあれこれ条件を指定させておいてから、 最期に「システムが混雑しているからできない」と出すようなシステムが最悪だ、 ということは誰に聞かなくても自明である。 では、どうすれば改善できるのか。 難しい問題だ。 もちろん、 システムが混雑するなどということが、 そもそもふざけた話なのだ。 もちろん、 そういう表示が画面に出たというだけの話であり、 本当にシステムが混雑しているのかどうかは分からないが、 理想的には、 システムが混雑しないように設計すればいいのである。 | ※ 「システムが混雑する」が何を意味しているかは、 専門家でないと正しく想像できないだろう。 私見であるが、 経験的に、おそらく殆どの場合、 このメッセージはネットワークの負荷が高くて応答に時間がかかっていることを意味すると予想する。 その原因としては、 最も多いのはシステムの設計上の欠陥やソフトウェアのバグである。 例えば、最初のリクエストがあったときだけに問い合わせればいい情報を、 毎回送信してみたり、 true/false の情報だけ get すればいいのに全レコードを送信させたり、 読み出すだけなのにロックをかけて他の人の処理を待たせたり、 というような事例がある。 | |
しかし、システムが本当に混雑することがあると仮定しよう。 混雑するという曖昧な表現は避けた方がいい。 システムが処理可能なリクエスト数を超えたリクエストを受け取る可能性がある、 ということにする。 ヘンな仮定ではあるが、 この場合、最も単純な方法として、 処理できるリクエストだけ受け付けるのが定番だ。 既にそうなっているじゃないかって? 違う。 区間を指定する前に、failさせるのが定番だと言ってるのだ。 このように処理したところで、 最期に「選択」を押した時にエラーメッセージが出ない保証はどこにもないが、 リクエスト数を適切に制限すれば、 かなりいい確率で問題を回避することはできるだろう。 もっと根本的に発想を変えてみることはできないか。 路線や駅名の選択処理と、 空席情報を調べる処理を、 独立した全く別のものにするのだ。 それぞれの枠内で処理を完結させるというのも、 Webでは定番の方法なのだが。 | ||
具体的には、 路線、駅名を選択させる処理が終了した時点で、 それらの情報を含んだURLだとかクッキーだとか、 何かそのようなものを生成する。 空席情報をgetする処理は、 CGIでも何でも構わないが、 路線、駅名を含んだ情報を使って呼び出す。 こうすれば、 仮に空席情報の処理で「混雑しています」のようなエラーが出た場合にも、 もう一度同じ情報を使って空席情報を呼び出すことができる。 駅名選択からやり直さなくてもすむのである。 | ※ 単純に、駅名を cgi のパラメータに直接渡す、 というような程度のものでも十分いいのではないか? | |
§ リファクタリングをしていると、 これとこれは共通なので上のクラスに引き上げたい、 みたいな状況がよく出てくる。 そんな無謀な表現だと訳の分かる人にしか分からないかもしれないが、 List 1のような場合だ。 ---- List 1 (最初の状態) ---- public class BaseClass { // BaseClass の処理 }; public class ClassA extends BaseClass { private String targetUrlString; // ClassA に特化した処理 }; public class ClassB extends BaseClass { private String targetUrlString; // ClassB に特化した処理 }; ---- List 1 end ---- なお今回はJavaで書いている。 両方のクラスに出てくる targetUrlString を BaseClass に移動して使いたい、 という話。 もちろん、そのような処理にするためには、 それぞれの派生クラス内での用途が完全に一致するという大前提があるべきである。 実際、そのような前提があろうがなかろうが知ったこっちゃない、 というような使い方ができないわけではないが、 そんなことをしたら後で呪いがかかることになっている。 ---- List 2 (基底クラスに引き上げる) ---- public class BaseClass { protected String targetUrlString; // ClassA、ClassB で共通に使う // BaseClass の処理 }; public class ClassA extends BaseClass { // ClassA に特化した処理 }; public class ClassB extends BaseClass { // ClassB に特化した処理 }; ---- List 2 end ---- | ||
List 2 のように、targetUrlString を基底クラスに移動するのだが、 private のままだとうまくいかないので、 ここでは protected に変更してあるが、 共通するメソッドごと移動して private のままにする手もある。 ともあれ、これで終われば話は平和なのだが、 実際は、この後に ClassC というクラスを追加したい、というようなこともある。 しかも、ClassC の中では、targetUrlString は全く使わないのだ。 そんなことに気付かなければ、 世の中平和なままで、このソースを使えばいいのだが、 ClassA と ClassB だけに共通なモノを ClassC も共有しているという事実に気付いた時点で、 気になって先に進めなくなってしまう。 その気になるコードがList 3。 | ※ ClassC を BaseClass から派生させること自体が間違い、 という解釈もあるだろうか? 明らかに、ClassC は is a BaseClass ではない。 ただ、その問題点が ClassC のものなのか、BaseClass のものなのか、 という意味ではファジーな部分が残る。 | |
---- List 3 (派生クラスを追加) ---- public class BaseClass { protected String targetUrlString; // ClassA、ClassB で共通に使う // BaseClass の処理 }; public class ClassA extends BaseClass { // ClassA に特化した処理 }; public class ClassB extends BaseClass { // ClassB に特化した処理 }; public class ClassC extends BaseClass { // ClassC に特化した処理 }; ---- List 3 end ---- もちろん、この問題を解決するのは簡単で、 List 4のように中間の派生クラスを追加すればいい。 ---- List 4 (中間の派生クラスを追加) ---- public class BaseClass { // BaseClass の処理 }; public class BaseClassAB extends BaseClass { protected String targetUrlString; // ClassA、ClassB で共通に使う }; public class ClassA extends BaseClassAB { // ClassA に特化した処理 }; public class ClassB extends BaseClassAB { // ClassB に特化した処理 }; public class ClassC extends BaseClass { // ClassC に特化した処理 }; ---- List 4 end ---- 理屈としては、これで最高に綺麗な方法で、 まぎれもなく問題ないという気はするのだが、 現実問題としては、そこまでしないとだめですカー?、 ということが肩に重くのしかかってくるのである。 というか、別にこれ位いいじゃん、みたいな。 とりあえず、 迷ったら「分けられるときは分けた方がいい」のではないかと思う。 |
(C MAGAZINE 2003年10月号掲載)
内容は雑誌に掲載されたものと異なることがあります。
修正情報:
2006-03-02 裏ページに転載。
(C) Phinloda 2003-2006, All rights reserved.