アプリを書いているとエラー処理や通知をどこに書くかで悩みます。本稿では、アプリに現れる「アクション」の概念を指摘し、アクションとエラー処理や通知の関係について述べます。
人によっては、こんなのは当たり前のことだと思われるかもしれません。しかし、筆者はアクションの概念を意識していなかったために設計をミスった経験があります。その反省として、この記事でこの概念の言語化を試みることにしました。
もしかしたら逆に全くの間違いだと言われるかもしれませんが、その際はコメントをいただけると嬉しいです。
おことわり: 本稿の「アクション」は一般的な用語ではありません。本稿の主張は筆者の経験に基づいて書かれており、普遍的に当てはまるわけではないかもしれません。
要約
- アクションは短時間で起こる一連の処理の最上位のこと
- アクションは例外をキャッチする場所
- アクションは結果を持たず、副作用を引き起こす
- アクションの Future は安全に捨てられる
- アクションは認知されている場合に限り実行状況や結果の通知を出す
アクションの概念
本稿の アクション は、平たく言うと 短時間で起こる一連の処理の最上位 を指します。
例えば Web アプリでは、HTTP リクエストを受け取ってから対応するレスポンスを返すまでの処理です。Web サーバが何日も動きつづけるのに対して、リクエストの処理は通常、数ミリ秒〜数分なので、短時間で起こると言って差し支えないでしょう。
逆に、Web サーバなどの通常は 短時間で停止しない処理はアクションとはいわない ことにします。
別の例としてイベント駆動型の GUI アプリでは、ボタンを押すなどのイベントが発生してから終了するまでの一連の処理が、1アクションになります。コンソールアプリならアプリの起動から終了までで1アクションかもしれません。
また、アクションは関数とは違って他のアクションの一部にはならないことにします。これは言い換えると、外側だけをアクションと呼ぶというわけです。
アクションのエラー
「例外は 最上位 でキャッチせよ」という主張をしばしば見ます。この「最上位」と本稿の「アクション」は近いものを指していると思います。
Web サーバが「HTTP リクエストを待ち受けている部分」より上で例外をキャッチせよとはいってないはずです。リクエストの処理中に起こった例外がその部分を停止させたら困ります。
したがって前述の主張を言い換えると、 アクションは例外をキャッチする場所 であるといえます。アクションを1個の関数として実装するなら、以下のように全体が try-catch で覆われることになるかもしれません。
function fooAction() {
try {
// アクションの処理
} catch (ex) {
// エラー情報をログ出力するかもしれない
// 「エラーが発生しました」などのメッセージを出すかもしれない
}
}
(場合によります。.NET なら集約例外ハンドラを使う方がいいかもしれません。)
アクションの結果
アクションは通常、1個の関数です。そして、結果型は void、unit、または int (終了コード) になります。
アクションの「結果」は関数の戻り値としてではなく、HTTP レスポンスの送信、画面への表示、ファイルの生成、データベースの更新、グローバル変数の書き換えなどの副作用として起こります。
アクションと非同期処理
非同期処理を async/await あるいは Future/Promise/Task によって「非同期的に何らかの処理をして、その結果を返す」パターンで実装するとします。
Future の結果を「受け取る」のも非同期なので、結果を受け取る側も非同期処理になります。しかし、すべての関数を非同期にすることはできません。そのためどこかには「非同期処理を開始するけど、その結果は無視する」ような場所があるはずです。(Fire & Forget. 言語によってはランタイム側にあるかもしれません。)
前述の通り、アクションからは例外が漏れず、特に結果値を持たないので、Future の結果を無視しても安全です。アクションは Future を捨てる場所 にふさわしいです。
アクションと通知
アクションを「ユーザーに認知されているアクション」とそうでないものの2種類に分けることにします。
ユーザーに認知されているアクション とは、ボタンを押すなどの、ユーザーが何らかの処理を期待する操作によって引き起こされるアクションです。
逆に認知されているとはいえないアクションは、例えばタイマーの指定時間が経過したとか、入力欄に文字を入力したとかです。ずいぶん大雑把ですが、アクションが認知されているかどうかはアプリの UI に依存するので、一概にはいえません。
傾向としては、認知されているアクションの処理が長引いているときは「処理中です」といった表示を出したり、エラーが発生したときは「エラーが発生しました」系のメッセージを出す方が、ユーザーをイライラさせないであろうと思います。
逆に認知されていないアクションがその手の通知を出してくるとうるさいかもしれません。
そのため 実行状況・実行結果の通知を出すか否かは、アクションが認知されているかどうかでおおよそ決まる といえそうです。
(最後に個人的な好みを書いておくと、メッセージ表示はモーダルダイアログではなくトーストにしてほしいし、GUI フレームワークはトースト通知を出す仕組みを用意しておいてほしいです。)