すると、ひよこの餌やりのタスクが終わったあとに1440分待機するような動作になる。ところが、このプログラムを使ったところ、ある日、ひよこの餌が出てこなくて、ひよこが全滅した。これにはお客さん、カンカンで...って、以前似たような話を書いたが*1、今回のケースは少し状況が違うので順番を追って書いていく。予告しておくけど、今回はこのあとひよこは何度も全滅するんだぜ...。最初ひよこが全滅したのかその原因を書いておこう。次図をご覧の通り、お客さんのイメージでは24時間ごとに餌やりをして欲しかったわけだ。お客さんの言う「定期的に」と言うのは、「(餌やりのタイミングが)一定間隔になっていて、その間隔が調整できること」が本来の要件だった。ところがこの要件が正しくヒアリング出来ていなくて弊社プログラマが次図のように実装してしまった。いう実装にしてしまうと「ひよこの餌やり」タスクが長引くと次の餌やりのタイミングが遅延してくる。間違った実装と言えるかも知れないが、後者のほうがお手軽な実装だし、タスクの内容によってはこれで事足りる場合もあるから、これがありえないような実装というわけでもないとは思う。今回のタスクはこの実装ではまずく結果としてひよこが全滅したということだ。ここまではヒアリング不足、要求仕様の理解不足と言えると思う。2度目のひよこの全滅そこで、上の前者の図(お客様の要望)のようになるようにプログラムを修正した。UIはさきほどと同一である次のUIのままである。ここでお客さんから待ったがかかった。前回のことで煮え湯を飲まされたお客さんは「あと何分後にひよこの餌やりタスクが実行される予定なのか画面に表示させて欲しい」という追加仕様を出してきたのである。これは「さもありなん」という感じで、「(次のタスクの実行が)あと何分後」という表示があれば、次のタスクの実行がきちんと予定されていることが目で見てわかるし、また、その表示があれば上の後者の図ではなく前者の図のように実装されているかのチェックにもなる。我々は最初からこのような表示をするべきだった。いうことで次図のようなダイアログに変更した。UpdateがあってOSが再起動した。これでひよこはまたもや全滅した。今回製作しているアプリ自体はOS起動時に自動的に起動するようになっている。アプリが起動していなかったので餌やりタスクが実行されなかったとかではない。アプリは定期的に状態を保存して(シリアライズして)、起動時に状態を戻す(デシリアライズする)。ゆえに、UI上の数値などはアプリを終了〜再起動した場合も前回の状態のままである。アプリが設定されたUI上の数値を保存していなかったなどでもない。XXXX分後です」の数字はインターバルタイマーで定期的に(1分ごとに)1を引く実装になっており、ここが0になった時点で再度タスクが実行されるという実装にしてあった。ところが、この実装だとアプリを終了させ、次回起動するまでの時間はノーカウントになってしまう。ゆえに、そのノーカウントである時間の分だけ餌やりのタイミングが遅れてしまうわけだ。Updateのあと、お客さんとしてはまだ実行を開始する時間じゃないからしばらくパソコンの電源を切ったままにしていたらしいのだが...。そこで正しい実装としては次のようになっているべきだったのだ。何分後に実行するのかのテキストボックスで設定された時間とやって次回予定実行時刻というのを算出する。アプリ終了時にはこの次回実行時刻を保存しておき、アプリ起動時にはこの次回実行時刻を復元する。現在時刻とやって算出して表示するべきだし、XXXXが0を下回って、かつ、「繰り返し実行する」のチェックボックスにチェックが入っていれば、そのタイミングで再度タスクが実行される。これで万事うまく行くように思えた。3度目のひよこの全滅このプログラムを長時間動作させているとまたもやひよこが全滅した。お客さんは、昼の12:00から24時間ごとにタスクを実行するつもりで1440分と設定していた。次第にタスク開始時刻が日に日に数秒ずつずれて来て、長期間運用すると何時間も開始時刻が昼の12:00からずれてしまっていた。現在時刻としていたためだ。次回実行予定時刻としなければタスク再実行〜現在時刻の取得までの分の誤差が累積してしまうというのが原因だった。3度目のひよこの全滅を目の当たりにしたお客さんは「毎日昼の12時にひよこの餌をやって欲しいだけなのに、なんでこんな簡単なこと出来ないの?馬鹿なの?死ぬの?」と怒り心頭に発した。(4)の修正をして、さすがに三度目の正直、これでうまく行くだろうと安易に考えていたのだが...。4度目のひよこの全滅お客さんは、タスクの開始時刻が12:00からずいぶんずれてしまったので、明日はまた12:00から開始したいとおっしゃるわけである。12:00にパソコンの前に正座して開始ボタンを押したくはないので、明日の12:00に開始されるように設定できないのかと言われたわけだ。設定できるようにしましょう。XXXX分後です」と表示されていたXXXXの部分をテキストボックスにしてここの値を変更できるようにした。ここの値は自由に変更できるので残り時間が明日の12:00までの時間になるように設定してもらえればいいですよ、と。そこでプログラムには次の処理を追加した。XXXXと再計算する。ところが、このようにアプリの仕様を変更したところ、ひよこがまた全滅した。お客さんが残り時間の計算を間違えて設定してしまったからだ。お客さんいわく「昼の12:00に実行したいだけであってそのために残り時間とか電卓で計算したくないんだよ!全滅したひよこは弁償してもらいますからね!」だそうで、確かに使いにくいUIかも知れないが、そのUIでいいと言ったのもあんただし、計算を間違えたのはあんただろと思いながらも、やはりこのUIには問題があることは認めざるを得なかった。12:00:00です」のようにDateTimeが表示されているべきだったのだ。変更したいという要望に対しては、この値をDateTimePickerで選択できるようになっているべきだったのだ。以上の試行錯誤のすえ、我々が最終的に行き着いたUIは次のUIだった。翌日の12:00になった。翌日も12:00になればきちんと処理が開始される。これにはお客さんも「大変素晴らしいアプリを作ってくれた!」と掌(てのひら)を返したように大喜び。ところが、喜びも束の間。数日後にまたもやひよこが全滅したのだ。お客さんが、ある日、ひよこ部屋の掃除のためにその日は手動で餌をやったのだ。アプリの起動を2日後に起動させられたこのアプリは、二日分のタスクを連続して実行した。通常の二倍の餌が1日で与えられたわけである。我々は、アプリが起動時に2つ以上のタスクが実行される予定になっているときは警告ダイアログを出すようにした。この条件を満たすとき、連続して2つ以上のタスクが実行されることになるので。6度目のひよこの全滅ある日、このお客さんの会社の重役の人がこのアプリを別PCにもインストールして別の施設のひよこの餌やりにも使うことにした。その人は、繰り返し設定として1440分後に実行するように設定した。「次回の実行予定時刻」を設定するのを忘れた。00:00:00になっている。この状態で実行するとタスクが2つ以上連続実行されるという警告ダイアログが出る。そこで次回の実行予定時刻のところを次回に実行して欲しい明日の12:00にセットした。その結果...またもやひよこが全滅した。タスクが実行されなかったのだ。「次回の実行予定時刻」のところはタスクの実行を開始した瞬間にXXXXだけ加算される。この表示は「次回の実行予定時刻」だが、開始ボタンを押す前に設定すべき値は「(今回の)実行予定時刻」なのだ。いますぐ実行を開始したいなら現在時刻になっていなければならない。この重役の人はカンカンで、「ここの値はアプリの初回起動時は現在時刻になっているべきではないのか」と言ってきた。あと「次回の実行予定時刻」という表記も紛らわしい。ひよこに餌をやるのを開始」というように変更した。別のお客さんからのクレームの嵐これらの処理を入れたのちは長期間この仕様で運用し、実績も出来、我々のUIもプログラムは完璧だと自負していた。他のお客さんからも「定期的に実行したいんだけど」と言われれば、必ずこのUIを提供することにした。ところが、他のお客さんにとっては、これが非常にわかりづらいらしく「もっとシンプルにならないの?」「何分後に実行するかだけ設定できればそれでいいんだけど?馬鹿なの?死ぬの?」など歴史は繰り返すわけである。実行したいんだけど」と言われるごとにこれらの話を説明するのが面倒なのでこのように記事にまとめておく次第である。まとめ「定期的にタスクを実行する」ためにはどういうUIを提供し、どういう設計にすればいいのか、私が直面した課題を一通り書いた。これにより、簡単に出来そうな内容なのに一筋縄ではいかないことがはっきりわかったと思う。私として、ここで書いたものが決定打だとは思っていないが、「タスクを定期実行したい」というありがちな要求仕様を満たす、一番シンプルなUIでありロジックだと思っている。お客さんからの受けはあまりよろしくない。これよりいいUI/設計があるならコメント欄で教えていただきたい。」と提案いただきました。*2これ、わかりやすいですね!こうやって皆さんの力で、「定期的に実行する」ためのスタンダードなUIが確立していくといいのですが。21:30追記はてブでお勧めされた。面白そうなのであとで読む。電子書籍になるに至るまでの話ブログちょっとだけ移転のお知らせ時代はまだゲームセンターあらしに追いつかないのかやねうら王、電王戦バージョン提出しました将棋の神様のレーティングはいくつなのか?月別アーカイブはてなブログをはじめよう!yaneuraoさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。